Sommaire

BONJOUR
Musique "Invention BWV988" J.S. BACH 1685-1750

Tutoriel et Help

THE LIBRARY


Tutoriel SDL Morpion

Installer et configurer une bibliothèque dans un projet

Guide complet pour configurer une bibliothèque en C et C++

  • . Introduction
    • I-A. Qu'est-ce qu'une bibliothèque ?
    • I-B. Comment une bibliothèque s'insère-t-elle dans le processus de compilation ?
    • I-C. Quelle est la différence entre bibliothèque statique et bibliothèque dynamique ?
    • I-D. Détails sur les bibliothèques
      • I-D-1. Organisation des fichiers
      • I-D-2. S'y retrouver dans les fichiers à télécharger
        • I-D-2-a. Convention de nommage
  • II. Configuration du projet
    • II-A. Arborescence du projet
    • II-B. Mise en place de la bibliothèque
      • II-B-1. GCC/G++ en ligne de commande
      • II-B-2. Code::Blocks
      • II-B-3. Visual Studio
      • II-B-4. Xcode
      • II-B-5. Qt Creator (QMake)
      • II-B-6. Eclipse CDT
      • II-B-7. CMake
        • II-B-7-a. Cas d'une bibliothèque installée sur le système
        • II-B-7-b. Cas d'une bibliothèque accompagnant le projet
      • II-B-8. Autotools
        • II-B-8-a. Cas d'une bibliothèque installée sur le système
        • II-B-8-b. Cas d'une bibliothèque accompagnant le projet
  • III. Erreurs
    • III-A. Implicit declaration of function…
    • III-B. Undefined reference
  • IV. Conclusion
  • V. Remerciements

Lorsque nous programmons en C ou C++, il arrive très rapidement que l'installation et la configuration d'une bibliothèque soient nécessaires pour la réalisation du projet. En effet, les bibliothèques apportent un grand nombre de fonctionnalités dont il serait bien dommage de se passer. Ce tutoriel explique comment rajouter une bibliothèque dans un projet.

I. Introduction

I-A. Qu'est-ce qu'une bibliothèque ?

Les bibliothèques peuvent être comparées à des boites à outils. En effet, celles-ci apportent des fonctionnalités supplémentaires que le programmeur va pouvoir utiliser pour réaliser son projet. Imaginons que vous souhaitiez afficher une image dans une fenêtre. Le langage C, ou même C++ en lui-même ne propose rien pour faire une telle chose. Sachant que d'autres programmeurs ont déjà travaillé dessus, il est préférable de réutiliser ce qu'ils ont fait plutôt que de perdre du temps à le refaire. Pour cela, une bibliothèque qui se spécialise dans l'affichage d'images nous fournira toutes les fonctions nécessaires pour lire une image et pour l'afficher. Au lieu d'effectuer les appels aux fonctions bas niveau comme fopen(), fread(), ou autres, il suffira d'appeler une fonction de la bibliothèque, qui lira directement le fichier sans même que l'on se préoccupe des détails.

L'autre bonne nouvelle, c'est que ce que vous voulez faire a sûrement été déjà réalisé et donc, qu'il existe une bibliothèque répondant à vos besoins.

Même les fonctions printf() ou pow() proviennent de bibliothèques. La première fonction provient de la bibliothèque C (libc), incluse par défaut dans tous projets et la seconde provient de la bibliothèque mathématiques (libm).

I-B. Comment une bibliothèque s'insère-t-elle dans le processus de compilation ?

Revoyons rapidement les étapes de la création d'un programme :

Image non disponible

Les fichiers source (les fichiers .c/.cpp/…) sont d'abord lus par le préprocesseur qui ne s'occupera que de traiter les commandes #XXX (#define, #ifdef…) pour produire un fichier source où ces directives ont été exécutées.

Ensuite, le compilateur transforme le source en un fichier intermédiaire (.o/.obj), compilé, mais qui n'est pas exécutable. Le langage utilisé est spécifique au compilateur. Pour valider votre code, le compilateur a besoin de connaître tous les symboles que vous utilisez. Le nom d'une fonction, autant que le nom d'une variable sont des symboles. Si vous utilisez une fonction qui est inconnue, il va vous le faire savoir avec un message d'erreur. En effet, pour lui, si la fonction est inconnue, il est incapable de traiter le symbole et donc, incapable de compiler le code. En C et C++, les fichiers .h/.hpp permettent de déclarer les symboles au compilateur et donc de lui dire : « cette fonction existe, voici les paramètres qu'elles acceptent, le type de valeur qu'elle retourne ».

Finalement, l'éditeur de liens prend les fichiers objets du programme pour les assembler et produire un exécutable. Ici, le code de toutes les fonctions doit être présent et sera inséré dans le programme. Si le code des fonctions que vous utilisez n'est pas dans vos fichiers objets, il va les chercher dans les bibliothèques que vous lui aurez indiquées. S'il ne trouve toujours pas la fonction, il ne peut pas continuer (le programme est incomplet) et l'édition des liens échouera.

Ainsi, pour pouvoir rajouter une bibliothèque à un projet, il faut :

  • les fichiers d'entêtes (.h/.hpp) décrivant les fonctions utilisables pour le compilateur ;
  • les fichiers de code compilé de la bibliothèque (.a/.so/.lib) pour l'éditeur de liens ;
  • les fichiers pour l'exécution (.so/.dll).

I-C. Quelle est la différence entre bibliothèque statique et bibliothèque dynamique ?

La bibliothèque statique sera compilée avec le programme et directement intégrée dans l'exécutable final. Il n'y aura donc pas besoin de .so/.dll lors de l'exécution. L'avantage de la compilation statique est de rendre l'exécutable indépendant (il peut fonctionner sur n'importe quelle plateforme compatible, sans pour autant nécessiter l'installation de la bibliothèque), toutefois, il est, de par ce fait, plus lourd et vous ne pourrez pas mettre à jour les bibliothèques sur lesquelles il repose, sans le recompiler.

La bibliothèque dynamique fera que l'exécutable ira chercher les fonctions à exécuter dans des fichiers séparés (.so/.dll). L'exécutable sera plus léger et les bibliothèques pourront être mises à jour sans recompiler l'exécutable. De plus, une bibliothèque peut être chargée à la volée (durant l'exécution d'un programme) et ainsi fournir des fonctionnalités en plus, si elle est présente : les systèmes de modules ou ajouts sont souvent implémentés sous la forme de bibliothèques chargées lorsque nécessaires.

Il est vrai que lors d'une compilation avec les bibliothèques dynamiques, celles-ci peuvent être mises à jour en remplaçant simplement les .so/.dll, toutefois, il faut que les changements entre les versions ne modifient en rien l'interface binaire de l'application (ABI). Soit, il ne faut pas que les signatures des fonctions changent.

I-D. Détails sur les bibliothèques

I-D-1. Organisation des fichiers

Sachant que les bibliothèques doivent toujours mettre à disposition les mêmes fichiers, celles-ci sont toujours présentées de la même façon. Au minimum, dans l'archive d'une bibliothèque, vous trouverez :

  • un dossier include, contenant les fichiers d'entêtes ;
  • un dossier lib, contenant les fichiers de code compilé à indiquer à l'éditeur de liens ;
  • un dossier bin (ou, directement à la racine), contenant les fichiers pour l'exécution d'un programme compilé avec cette bibliothèque (.dll/.so).

Bien entendu, vous pouvez aussi trouver un fichier README, un fichier de licence et de la documentation.

I-D-2. S'y retrouver dans les fichiers à télécharger

Le site officiel d'une bibliothèque vous proposera de nombreux fichiers à télécharger. Chaque fichier porte un nom bien précis permettant de trouver rapidement celui correspondant à vos besoins.

La raison de cette multitude de fichiers est que chaque compilateur nécessite un fichier compilé par un même compilateur à la même version. En effet, les .lib compilés avec Visual Studio 2012, ne seront pas compatibles avec le compilateur MinGW, ou même, ils ne seront pas compatibles avec Visual Studio 2008.

Voici les fichiers proposés sur le site de la SDL (bibliothèque de jeux 2D) :

Classification | Fichier | Description
Binaire pour l'exécution (« Runtime Binaries ») | SDL2-2.0.1-win32-x86.zip | Fichier contenant la DLL pour Windows 32 bits.
 | SDL2-2.0.1-win32-x64.zip | Fichier contenant la DLL pour Windows 64 bits.
 | SDL2-2.0.1.dmg | Fichier de développement et d'exécution pour OS X.
Fichiers pour le développement (« Development Libraries ») | SDL2-devel-2.0.1-VC.zip | Fichiers de développement pour Visual C++ (Visual Studio).
 | SDL2-devel-2.0.1-mingw.tar.gz | Fichiers de développement pour MinGW.
 | SDL2-2.0.1.dmg | Fichier de développement et d'exécution pour Mac OS.
Sources | SDL2-2.0.1.zip SDL2-2.0.1.tar.gz | Code source de la bibliothèque. En la compilant, vous générerez les fichiers .lib/.so/.a/.dll/…

Il se peut que les fichiers proposés diffèrent quelque peu, mais généralement, les bibliothèques se ressemblent.

Sous Linux, il est possible d'installer les bibliothèques à l'aide du gestionnaire de paquets.

Une erreur commune, provoquant l'apparition d'« undefined referenceUndefined reference » malgré la bonne configuration du projet, est d'utiliser des bibliothèques 64 bits alors que le compilateur ne supporte que le 32 bits. En effet, certains compilateurs ne sont pas capables de gérer les formats 64 bits et donc ne les liront simplement pas (et ce bien que vous ayez un système d'exploitation 64 bits). Comme les fichiers de la bibliothèque ne sont pas lus, les fonctions définies dans le fichier ne seront pas reconnues, provoquant des erreurs de type « undefined reference ».

De plus, sous Windows, vous aurez l'erreur 0xc000007b lorsque vous aurez un projet compilé en 32 bits, tout en ayant les .DLL en 64 bits.

I-D-2-a. Convention de nommage

Les fichiers à télécharger se différencient par l'architecture, mais aussi par les spécificités de compilation donc voici la convention de nommage :

Nommage | Description
x86 | Bibliothèque 32 bits.
x86-64 | Bibliothèque 64 bits.
d (ou debug) | Bibliothèque compilée avec les informations de débogage.
mt (ou t) | Bibliothèque utilisant du multithread.
static | Bibliothèque statique.
monolith | Bibliothèque où tous les modules ont été regroupés en un bloc.

II. Configuration du projet

II-A. Arborescence du projet

Afin que votre projet soit organisé et facilement distribuable, nous allons créer les dossiers suivants :

  • lib : pour les fichiers des bibliothèques (.a/.so/.lib) ;
  • include : pour les fichiers d'entêtes (notamment, ceux de la SDL) ;
  • src : pour vos fichiers de code source.

Cette organisation permet d'éviter que vous ayez à installer (ou faire installer) la bibliothèque sur chacun des postes que vous utiliserez : elle est intégrée dans le projet. De plus, l'installation de la bibliothèque dans les répertoires des éditeurs n'est pas recommandée (même si certains sites font de cette façon :(), notamment, car vous forcez l'utilisateur recevant votre projet à installer lui-même la bibliothèque et à reconfigurer le projet, mais aussi, parce que cela entraîne des oublis. D'autant plus que sous Windows, il n'y a pas d'emplacement déterminé pour mettre les bibliothèques.

Ce qui nous donne l'arborescence suivante :

/
include
- Nom bibliothèque 1
- - …
- - tous les fichiers .h de la première bibliothèque
- - …
- Nom bibliothèque 2
- - …
- - tous les fichiers .h de la deuxième bibliothèque
- - …
- …
lib
- libXXX.so (dynamique) ou libXXX.a (statique) (pour Linux)
- libXXX.lib (pour Windows) ou libXXX*.a (pour MinGW)
- …
src
- main.c
XXX.dll (pour Windows)
les fichiers de projets (.cbp (pour Code::Blocks), .sln (pour Visual Studio)…)

Lorsque vous voulez distribuer le code de votre projet, il suffit de compresser le dossier et c'est tout. L'utilisateur à qui vous donnez votre projet n'aura qu'à décompresser le fichier et il pourra directement l'utiliser.

II-B. Mise en place de la bibliothèque

La mise en place d'une bibliothèque se fait en trois étapes :

  • spécification d'un nouveau répertoire de fichiers d'entêtes pour le compilateur ;
  • spécification d'un nouveau répertoire de fichiers de bibliothèques pour l'éditeur de liens ;
  • spécification des fichiers de bibliothèques à utiliser.

II-B-1. GCC/G++ en ligne de commande

Dans votre ligne de compilation :

  • rajoutez une ou plusieurs options -I pour indiquer le répertoire des fichiers d'entêtes ;
  • rajoutez une ou plusieurs options -L pour indiquer le répertoire des fichiers des bibliothèques ;
  • rajoutez une ou plusieurs options -l pour indiquer les fichiers de bibliothèques à utiliser. Généralement, le nom est formé avec le motif suivant : libXXX.so (XXX étant le nom de la bibliothèque). Le nom à passer à l'option est simplement XXX : -lXXX.

Ce qui donne :

gcc -Wall -Wextra -L./lib -I./include src/main.c -o main -lXXX

Pour compiler le fichier src/main.c avec la bibliothèque XXX.

II-B-2. Code::Blocks

Dans votre projet Code::Blocks, dans les options de compilation (« build options ») :

Image non disponible

Ensuite, pensez bien à sélectionner votre projet dans son intégralité et non pas l'une des cibles (par défaut « debug » ou « release »), cela permettra d'appliquer la configuration à l'intégralité du projet et de ne pas avoir de problème lors d'un changement de cible :

Image non disponible

Maintenant, vous pouvez ajouter le répertoire des fichiers d'entêtes dans l'onglet « répertoires de recherche » (« search directories »), sous onglet « compilateur » (« compiler »). Cliquez sur le bouton « ajouter » (« Add ») et tapez : ./include puis « OK ».

Image non disponible

Il faut maintenant faire de même pour le répertoire des bibliothèques, dans le sous-onglet « Éditeur de liens » (« Linker »), ajoutez ./lib :

Image non disponible

Finalement, vous pouvez rajouter les bibliothèques, dans l'onglet « paramètres de l'éditeur de liens » (« Linker settings ») :

Image non disponible

Votre projet est finalement prêt pour utiliser la bibliothèque.

II-B-3. Visual Studio

Dans les propriétés du projet « Projet » → « Propriétés » (« Project » → « Properties ») :

Image non disponible

Cliquez sur « Propriétés de la configuration » (« Configuration Properties ») afin de déplier l'arborescence et de déverrouiller les deux options en haut de la fenêtre. Sélectionnez la configuration « Toutes plateformes » (« All Configurations ») afin que vos modifications s'appliquent sur la cible « Debug » et la cible « Release » :

Image non disponible

Dans le menu C/C++, il est conseillé de changer l'entrée « Niveau d'avertissement » (« Warning Level ») du niveau 3 au niveau 4 afin que le compilateur vous renvoie le maximum d'informations sur votre code et ses possibles erreurs.

Dans le menu C/C++, rajoutez ./include pour l'entrée « Répertoires d'inclusion supplémentaires » (« Additional Include Directories ») :

Image non disponible

Dans le menu « Éditeur de liens » (« Linker »), ajoutez ./lib pour l'entrée « Répertoires de bibliothèques supplémentaires » (« Additional Library Directories ») :

Image non disponible

Dans le sous-menu « Entrée » (« Input »), pour l'entrée « Dépendances supplémentaires » (« Additional Dependencies »), insérez « libXXX.lib » :

Image non disponible

Pour valider chaque modification des propriétés, cliquez sur le bouton « Appliquer » (« Apply »).

Afin que vous puissiez copier votre projet d'un répertoire à un autre et que Visual Studio continue de trouver les fichiers, il est recommandé d'utiliser des chemins relatifs (par exemple : ./lib et ./include).

Vous devriez maintenant pouvoir compiler le projet utilisant la bibliothèque.

II-B-4. Xcode

Dans Xcode, cliquez sur votre projet dans l'arborescence se trouvant à gauche de l'éditeur afin de faire apparaître les propriétés. Ensuite cliquez sur « Paramètres de compilation » (« Build Settings ») et rajoutez /Libraries/Frameworks à l'entrée « Chemin de recherche des frameworks » (« Framework Search Paths ») permettant ainsi à Xcode de trouver le fichier Framework de la bibliothèque. Dans l'onglet « Étapes de compilation » (« Build Phases ») et dans la sous-section « Lier binaire avec des bibliothèques » (« Link Binary With Libraries »), cliquez sur le bouton « Ajouter autre… » (« Add Other… ») puis dans le dossier /Library/Frameworks, trouvez la framework de votre bibliothèque. Votre projet est maintenant prêt à être compilé.

II-B-5. Qt Creator (QMake)

La configuration d'un projet Qt Creator utilisant QMake se fait à travers la modification du fichier .pro de votre projet.

Pour rajouter un dossier de fichiers d'entêtes, vous devez utiliser la variable INCLUDEPATH :

Sélectionnez
INCLUDEPATH += ./include

Pour rajouter un dossier de bibliothèques, vous devez utiliser la variable LIBS :

Sélectionnez
LIBS += -L./lib

Finalement, pour rajouter une bibliothèque à utiliser lors de l'édition des liens, vous devez utiliser une nouvelle fois la variable LIBS :

Sélectionnez
LIBS += -lXXX

N'oubliez pas d'exécuter le programme qmake pour que votre projet prenne en compte les modifications du .pro.

II-B-6. Eclipse CDT

Dans Eclipse CDT, vous pouvez accéder aux propriétés du projet en passant par le menu « Projet » → « Propriétés » (« Project » → « Properties »), ou en faisant un clic droit sur votre projet puis « Propriétés » (« Properties ») :

Image non disponible

Afin de modifier toutes les configurations en une seule fois, vous devez choisir la configuration « Toutes les configurations » (« All configurations ») :

Image non disponible

Ensuite, vous devez accéder par l'arborescence sur la gauche à « Compilation C/C++ » → « Paramètres » (« C/C++ Build » → « Settings »).

Dans l'onglet « Paramètrages des outils » (« Tool Settings »), vous devez sélectionner « Entêtes » (« Includes ») pour ajouter le dossier des fichiers d'entêtes :

Image non disponible

La configuration du dossier des bibliothèques et des bibliothèques à inclure dans votre projet se trouve dans l'entrée « Bibliothèques » (« Libraries ») :

Image non disponible

Afin d'obtenir le chemin sous la forme « "${workspace_loc:/${ProjName}/lib}" » il suffit de cliquer sur le bouton « Workspace » lors de l'ajout d'un répertoire et de sélectionner le répertoire voulu du workspace comme le montrent ces captures d'écrans :

Image non disponible Image non disponible

Pour finir, il suffit de valider toutes les fenêtres de configuration et votre projet est prêt.

II-B-7. CMake

CMake permet de générer les fichiers pour compiler un projet (fichiers de Visual Studio, Code::Blocks…) en écrivant un unique fichier descriptif du projet à compiler.

II-B-7-a. Cas d'une bibliothèque installée sur le système

Si la bibliothèque est installée (ou doit être installée) sur le système, CMake peut rechercher lui-même la bibliothèque et configurer le projet en adéquation avec votre système grâce à la commande Image non disponible FindPackage :

Sélectionnez
FindPackage(XXX)

Toutefois, la commande FindPackage nécessite la présence d'un fichier descriptif indiquant à CMake comment trouver et configurer la bibliothèque. Généralement, l'installation de CMake s'accompagne d'une série de ces fichiers pour les bibliothèques les plus courantes.

II-B-7-b. Cas d'une bibliothèque accompagnant le projet

Pour spécifier un répertoire de fichiers d'entêtes, il faut utiliser include_directories :

Sélectionnez
include_directories(./include)

Pour spécifier un répertoire de bibliothèques, il faut utiliser link_directories :

Sélectionnez
link_directories(./lib)

Finalement, pour ajouter une bibliothèque, il faut utiliser :

Sélectionnez
target_link_libraries(
    executable
    XXX)

Voilà, vous pouvez générer les fichiers de compilation de votre projet.

II-B-8. Autotools

Autotools permet lui aussi de générer les fichiers de compilation pour un projet. Pour rajouter une bibliothèque à votre projet, il faut éditer le fichier Makefile.am.

II-B-8-a. Cas d'une bibliothèque installée sur le système

Si la bibliothèque est installée (ou doit être installée) sur le système, autotools propose une macro qui vérifiera la présence de la bibliothèque et configurera le compilateur en adéquation :

Sélectionnez
AC_CHECK_LIB([XXX], [functionXXX], [AC_SUBST(LIBDL,-lXXX)])

Vous pouvez aussi utiliser AC_SEARCH_LIB :

Sélectionnez
AC_SEARCH_LIBS(functionXXX, XXX, [], [
    AC_MSG_ERROR([XXX est introuvable])
])

II-B-8-b. Cas d'une bibliothèque accompagnant le projet

Si la bibliothèque accompagne votre projet et n'est donc pas disponible pour le système, pour ajouter un répertoire de fichiers d'entêtes, il faut utiliser la variable _CFLAGS (pour du C) ou _CPPFLAGS (pour du C++) :

Sélectionnez
exec_CFLAGS=-I./include

ou

Sélectionnez
exec_CPPFLAGS=-I./include

Pour ajouter un répertoire de bibliothèques et une bibliothèque, il faut utiliser la variable _LDADD :

Sélectionnez
exec_LDADD=-L./lib -lXXX

(Dans le cas, où votre exécutable final s'appelle « exec »)

III. Erreurs

III-A. Implicit declaration of function…

L'erreur « Implicit declaration of function… » apparaît lorsque vous n'avez pas rajouté le fichier d'entêtes dans votre code source à l'aide d'un #include. En effet, comme tout autre programme, il faut inclure le fichier d'entêtes dans le code pour avoir accès aux fonctions (sinon, le compilateur ne connaît pas ces symboles).

III-B. Undefined reference

L'erreur « undefined reference » indique que l'éditeur de liens ne trouve pas une fonction. Cela arrive si :

  • vous n'avez pas ajouté la bibliothèque au projet (ou il manque une autre bibliothèque, Google pourra vous aider à trouver de laquelle dont il s'agit, en recherchant « undefined reference le_nom_de_la_fonction ») ;
  • si le fichier de la bibliothèque n'est pas trouvé ou qu'il n'est pas lu (il n'est pas lu, lorsqu'il n'est pas compatible avec votre compilateur).

IV. Conclusion

Vous savez maintenant installer et configurer une bibliothèque dans votre projet. Vous pouvez continuer le développement de votre programme tout en utilisant les fonctionnalités de cette bibliothèque.


Tutoriel SDL Morpion

Bibliothèque logicielle

En informatique, une bibliothèque logicielle est une collection de fonctions, qui peut être déjà compilée et prête à être utilisée par des programmes [1],[2]. Les bibliothèques sont enregistrées dans des fichiers semblables, voire identique aux fichiers de programmes[3], sous la forme d'une collection de fichiers de code objet rassemblés[2] accompagnée d'un index permettant de retrouver facilement chaque fonction[3].

Les bibliothèques sont apparues dans les années 1950, et sont devenues un sujet incontournable de programmation. Elles sont utilisées pour réaliser des interfaces de programmation, des framework, des plugins ainsi que des langages de programmation. Les fonctions contenues dans les bibliothèques sont typiquement en rapport avec des opérations fréquentes en programmation : manipulation des interfaces utilisateur, manipulation des bases de données ou les calculs mathématiques[2].

Les bibliothèques sont manipulées par l'éditeur de lien et le système d'exploitation. Les manipulations sont différentes suivant que la bibliothèque est statique ou partagée. Les emplacements et les noms des bibliothèques varient selon les systèmes d'exploitation.

  • 1 Utilisation
  • 2 La technique
  • 3 En pratique
  • 4 Notes et références
  • 5 Voir aussi

Utilisation

En informatique une fonction est un ensemble d'instructions qui prend en charge une certaine opération et produit un résultat[4]. Les bibliothèques sont apparues dans les années 1950, sous la forme d'un magasin contenant des lots de cartes perforées ou des rouleaux de bande magnétique sur lesquels étaient enregistrées des routines - fonctions[3]. Les programmeurs pouvaient choisir les bandes magnétiques à utiliser dans leurs programmes[3].

Les bibliothèques sont utilisées pour mettre en œuvre des interfaces de programmation [5] ainsi que des frameworks et des plugins et peuvent être incorporées dans les langages de programmation.

Une interface de programmation (abr. API pour Application Programming Interface) est un ensemble documenté et standardisé de fonctions par lesquelles un logiciel offre des services à d'autres logiciels. Les fonctions sont mises à disposition, compilées et emballées sous la forme d'une bibliothèque logicielle. La documentation spécifie comment le logiciel consommateur peut interagir avec le logiciel fournisseur en utilisant les fonctions[5]. Les interfaces de programmation sont un sujet incontournable de la programmation contemporaine et un logiciel se sert typiquement de nombreuses interfaces de programmation[5].

Les frameworks sont des collections de classes (lots de fonctions) semi-finies qui, utilisées ensemble, forment le squelette d'un programme. Ils permettent la construction d'un programme par assemblage et dérivation de composants[6]. Les frameworks se distinguent des interfaces de programmation par le fait que les interfaces de programmation comportent des fonctions qu'un programme va utiliser, tandis que les framework sont des bibliothèques qui utiliseront les fonctions du programme[7].

Les plugins sont des bibliothèques logicielles qu'une application recherche en vue d'étendre ses fonctionnalités. Cette technique est utilisée par exemple par les navigateurs web pour prendre en charge l'affichage d'animations ou de vidéos[5].

Programmer et exploiter des bibliothèques fait partie des utilisations ordinaires des langages de programmation généralistes. Si dans les premiers langages de programmation les fonctionnalités étaient délimitées par le lexique du langage, les langages de programmation récents ont un lexique très réduit et beaucoup de fonctionnalités sont apportées par des bibliothèques standard qui accompagnent le langage de programmation[8]. Par exemple la bibliothèque standard de C contient une collection normalisée de fonctions permettant de manipuler des fichiers, d'écrire des textes, d'utiliser la mémoire, ou de faire des calculs[5].

La technique

Les bibliothèques sont manipulées de manière différente par l'éditeur de liens et le système d'exploitation selon si elles sont statiques ou partagées. La manipulation se sert de la table de symboles créée par le compilateur.

bibliothèque statique

Une bibliothèque est dite statique si elle est destinée à être copiée dans les programmes qui l'utilisent lors de la construction de ces derniers[1].

bibliothèque partagée

Une bibliothèque est dite partagée si elle est destinée à être associée aux programmes au moment où ils sont exécutés. Avec une telle bibliothèque, la même copie de la bibliothèque peut être utilisée par plusieurs programmes[1].

éditeur de liens

L'éditeur de liens est le programme qui combine différents fichiers de code objet, parmi lesquels des bibliothèques, pour en faire un programme exécutable[9].

table de symboles

Une table de symboles est une structure manipulée par les compilateurs et les éditeurs de liens, qui contient les noms d'éléments du programme (fonctions, variables, constantes), ainsi que leur adresses, c'est-à-dire leur emplacement dans le programme[9]. La table de symboles produite par le compilateur est utilisée ensuite par l'éditeur de liens pour déterminer l'emplacement des fonctions qui se trouvent dans les bibliothèques[9].

fonction bouchon (anglais stub)

Une fonction bouchon est une fonction f'() ajoutée automatiquement à un programme qui ne fait rien d'autre que d'utiliser une fonction f() d'une bibliothèque partagée[9].

Résolution de noms

Pour qu'un programme utilise une fonction, il est nécessaire que la provenance de la fonction soit connue. La provenance est déterminée conjointement par le compilateur, l'éditeur de liens et le système d'exploitation, dans un processus de résolution qui recherche dans les bibliothèques. Le processus diffère selon qu'il s'agit de bibliothèques statiques ou partagées.

Tout d'abord, lors de la traduction d'un fichier de code source en code objet par un compilateur, celui-ci ajoute à la table de symboles les noms des fonctions utilisées dans ce fichier source ainsi que leur adresse[9]. L'adresse est laissée vide si la fonction n'a pas été trouvée dans le fichier de code source[9]. Puis l'éditeur de liens recherchera alors à quoi correspond chaque fonction dont l'adresse est laissée vide par le compilateur[1].

Si la fonction provient d'une bibliothèque dite statique, l'éditeur de liens copie l'intégralité de la bibliothèque dans le programme[1]. Le fichier de bibliothèque n'est alors plus nécessaire à l'exécution du programme[1]. La bibliothèque est ainsi copiée dans chaque programme qui l'utilise[1].

Si la fonction provient d'une bibliothèque partagée alors l'éditeur de liens ne la copie pas dans le programme, au lieu de cela le système d'exploitation placera la bibliothèque en mémoire en même temps que le programme, c'est-à-dire immédiatement avant son exécution[1]. Une seule copie de la bibliothèque est enregistrée dans l'ordinateur[1]. Lorsque plusieurs programmes utilisant cette bibliothèque se trouvent en mémoire, ils utilisent tous la même copie de la bibliothèque, ce qui permet d'économiser de la place par rapport à une bibliothèque statique[1].

L'utilisation de bibliothèques partagées présente cependant certains risques. Le principal risque est qu'une bibliothèque utilisée par un programme ne peut pas être trouvée au moment où le programme en a besoin, entraînant l'échec du programme[1]. Il arrive également parfois que l'interface de programmation de la bibliothèque qui a été trouvée ne corresponde pas à celle dont le programme a besoin, entraînant un crash de ce dernier[1].

Il est parfois nécessaire de recalculer l'adresse de chaque fonction après l'avoir ajoutée au programme[9] ou de créer des fonctions de bouchon.

Avec une bibliothèque statique, le recalcul des adresses est effectué par l'éditeur de liens immédiatement après la compilation. Tandis qu'avec une bibliothèque partagée il est effectué lors du chargement du programme en mémoire, immédiatement avant son exécution[10]. Le position independent code (abr. PIC) est un code objet produit par certains compilateurs, qui ne nécessite pas de recalcul de ces adresses par l'éditeur de liens. Ce type de code est souvent utilisé pour les bibliothèques[9].

Lors de la création d'une fonction bouchon, l'éditeur de liens ajoute ensuite une entrée dans la table de symboles du programme pour informer que l'adresse de la fonction référencée doit être recalculée avant l'exécution du programme[9].

En pratique

Programmer des bibliothèques fait partie des utilisations ordinaires des langages de programmation généralistes. Si dans les premiers langages de programmation les fonctionnalités étaient délimitées par le lexique du langage, les langages de programmation récents ont un lexique très réduit et beaucoup de fonctionnalités sont apportées par des bibliothèques standard qui accompagnent le langage de programmation[8].

Lorsqu'un ou plusieurs programmes utilisent une bibliothèque dynamique, une seule copie du code et des ressources de cette bibliothèque est chargée en mémoire. La même fonction peut potentiellement être exécutée simultanément par plusieurs programmes et les variables globales peuvent potentiellement être modifiées simultanément[11]. Deux qualités recherchées des fonctions des bibliothèques sont d'être thread-safe et/ou réentrantes.

  • réentrant signifie que la fonction continue de donner des résultats corrects quand elle est exécutée plusieurs fois simultanément.
  • thread-safe signifie que la fonction continue de donner des résultats corrects quand elle est exécutée simultanément par plusieurs threads.

L'utilisation simultanée par plusieurs programmes d'une fonction qui n'est pas thread-safe ni réentrante peut entrainer des pannes par situation de compétition (anglais race condition)[12].

Les noms des fichiers de bibliothèque partagées se terminent par .so sur Unix, .dylib sur Mac OS X et .dll sur Windows [5], et les emplacements conventionnels des bibliothèques diffèrent en fonction de la plateforme.

Sur les systèmes d'exploitation Unix les bibliothèques sont en principe enregistrées dans les répertoires /lib ou /usr/lib. Une variable d'environnement LD_LIBRARY_PATH permet d'indiquer d'autres répertoires à prendre en compte par l'éditeur de liens. Le nom de chaque bibliothèque comporte les lettres lib et se termine par .a (bibliothèque statique) ou .so (bibliothèque partagée)[2]. Les bibliothèques statiques utilisent le format des fichiers d'archive[3]. Ce format, théoriquement destiné à stocker une collection de fichiers quelconques, est utilisé exclusivement pour les fichiers de bibliothèque[3].

Sur les systèmes d'exploitation Windows, les bibliothèques statiques ont un format semblable au format d'archive des systèmes Unix, mais avec un index de construction différente[3]. Les noms des bibliothèques statiques se terminent par .lib tandis que les noms des bibliothèques dynamiques se terminent par .dll. Une bibliothèque statique peut également être une collection de bouchons relatifs à une bibliothèques dynamique. Les noms des bibliothèques dynamiques peuvent également se terminer par .ocx, .drv ou .cpl[5]. Les emplacements standard des bibliothèques sont les répertoires \Windows, ou \Windows\System, ainsi que le répertoire ou se trouve l'application qui utilise la bibliothèque. Une variable d'environnement permet d'indiquer au système d'exploitation d'autres répertoires éventuels[13].

Sur les systèmes d'exploitation Mac OS X les bibliothèques sont distribuées sous forme de framework: un répertoire dans lequel se trouve la/les bibliothèques ainsi que la documentation et les headers (description des fonctions dans un langage de programmation). La plupart des bibliothèques logicielles de Mac OS X (Cocoa, Carbon…) sont distribuées sous cette forme[5]. Les bibliothèques sous forme de framework sont enregistrées dans le répertoire /System/Library/Frameworks, et les pilotes sont enregistrés dans le répertoire /System/Library/Extensions[14].

Notes et références

  1. ↑ a, b, c, d, e, f, g, h, i, j, k et l(en)Brian Ward, How Linux Works: What Every SuperUser Should Know, No Starch Press - 2004,(ISBN 9781593270353)
  2. ↑ a, b, c et d(en)Neil Matthew et Richard Stones, Beginning Linux Programming, John Wiley & Sons - 2011,(ISBN 9781118058619)
  3. ↑ a, b, c, d, e, f et g(en)John Levine, Linkers and loaders - Operating Systems Series, Morgan Kaufmann - 2000,(ISBN 9781558604964)
  4. ↑ (en)John Shapley Gray, Interprocess Communications in Linux, Prentice Hall Professional - 2003,(ISBN 9780130460424)
  5. ↑ a, b, c, d, e, f, g et h (en)Martin Reddy, API Design for C++, Elsevier - 2011,(ISBN 9780123850041)
  6. ↑ (en)Stephen D. Huston - James C. E. Johnson - Umar Syyid,The ACE Programmer's Guide: Practical Design Patterns for Network and Systems Programming,Addison-Wesley Professional - 2004,(ISBN 9780201699715)
  7. ↑ (en) Richard Harrison et Mark Shackman, Symbian OS C++ for Mobile Phones, Volume 3, John Wiley & Sons - 2007,(ISBN 9780470066584)
  8. ↑ a et b (en)David A. Watt,Programming Language Design Concepts,John Wiley & Sons - 2004,(ISBN 9780470020470)
  9. ↑ a, b, c, d, e, f, g, h et i (en)Santanu Chattopadhyay,System Software, PHI Learning Pvt. Ltd. - 2007,(ISBN 9788120330511)
  10. (en)Shibu,Intro To Embedded Systems 1E,Tata McGraw-Hill Education,(ISBN 9780070145894)
  11. ↑ (en)Arndt Bode,Euro-Par 2000 Parallel Processing: 6th International Euro-Par Conference, Munich, Germany, August 29-September 1, 2000,Springer - 2000,(ISBN 9783540679561)
  12. ↑ (en)Dick Buttlar - Jacqueline Farrell - Bradford Nichols,PThreads Programming: A POSIX Standard for Better Multiprocessing, O'Reilly Media, Inc. - 2013,(ISBN 9781449364748)
  13. ↑ (en)Brian Hook,Write portable code: an introduction to developing software for multiple platforms,No Starch Press - 2005,(ISBN 9781593270568)
  14. ↑ (en)Amit Singh,Mac OS X Internals: A Systems Approach,Addison-Wesley Professional - 2006,(ISBN 9780132702263)

Voir aussi

Articles connexes

  • Framework
  • Interface de programmation
  • éditeur de liens

v · d · m

Éléments de programmation informatique

Bibliothèque logicielle

Bibliothèque standard • Espace de nom • Framework • Gabarit (template) • Interface • Interface de programmation (API)

Vocabulaire

Algorithme • Instruction • Définition d'opérateur • Expression • Indentation • Ligne de code • Opérateur • Pseudo-code
Autour des fonctions | Convention de nommage • Dispatch multiple • Factorisation • Fonction imbriquée • Fonction de rappel • Fonction récursive • Généricité • Opérande • Paramètre • Polymorphisme • Procédure • Signature de type • Surcharge
Autour de l'objet | Classe • Constructeur • Destructeur • Encapsulation • Héritage • Héritage multiple • Instance • Méthode • Ramasse-miettes (garbage collector) • Référence

Dans le code source

Structures de données | Arbre • Attribut • Caractère • Enregistrement • File • First in, first out (fifo) • Last in, first out (lifo) • Liste • Liste chaînée • Pile • Propriété • Tableau • Tas • Type abstrait • Sémaphore
Déclarations : types et variables | Affectation • Pointeur • Portée • Tableau associatif • Type énuméré • Type récursif • Typage statique • Variable • Variable globale • Variable locale
Structures de contrôle | case • do • else • eval • if • for • goto • loop • switch • while
Fonctions usuelles | Concaténation • Incrémentation • malloc • printf

Outil de développement logiciel

Environnement de développement • Générateur de documentation • Gestion de versions • Modèle • Patch • Spécification

Folklore


DDL Wiki

Dynamic Link Library

Dynamic Link Library soit une bibliothèque de liens dynamiques est un format de fichiers (extension dll) pour les bibliothèques logicielles communes aux systèmes d'exploitation Microsoft Windows et IBM OS/2.

Ces bibliothèques logicielles existent dans Windows depuis 1985 et utilisent la technique des liens dynamiques (en anglais : dynamic link) pour permettre de les associer avec les programmes qui s'en servent.

Les fichiers DLL sont une des fondations des systèmes d'exploitation Windows et ils sont utilisés pour les interfaces de programmation, les pilotes, les widgets et les polices de caractères.

  • 1 Fonctionnement
  • 2 Dans Windows
  • 3 Programmation
  • 4 DLL hell - l'enfer des DLL
  • 5 Notes et références
  • 6 Voir aussi

Fonctionnement

Un programme informatique est construit à partir d'un ensemble de fichiers source contenant des fonctions. Les bibliothèques logicielles sont des lots de fonctions stockées dans des fichiers. Une bibliothèque logicielle contient généralement un ensemble de fonctions autour du même sujet. Par cette technique, une modification subséquente d'une des fonctions de la bibliothèque nécessite de la re-créer, sans qu'il soit nécessaire de re-créer la totalité de l'application.

Cette technique nécessite de charger en mémoire les fichiers de bibliothèque en même temps que le programme qui les utilise[8], ensuite de quoi un programme ?#8364;” l'éditeur de liens ?#8364;” rend l'application opérationnelle en tissant des liens entre les différents fichiers.

Les bibliothèques logicielles peuvent être :

  • Statiques :

Une bibliothèque statique est incorporée dans un programme et l'édition de liens est effectuée pendant sa construction. Lorsqu'une bibliothèque statique est modifiée, chaque programme qui l'utilise doit être reconstruit pour incorporer les modifications[7] ;

  • Partagées :

Une bibliothèque partagée n'est pas incorporée dans les programmes qui l'utilisent, mais est reliée à ces programmes lors de leur chargement, c'est-à-dire immédiatement avant l'exécution du programme. Une bibliothèque partagée peut être utilisée par de nombreux programmes et une modification de la bibliothèque ne nécessite pas de reconstruction des programmes, du moment que l'interface de programmation de la bibliothèque ne change pas;

  • Dynamiques :

Les bibliothèques dynamiques sont identiques aux bibliothèques partagées, mais sont utilisées d'une manière différente. De telles bibliothèques sont reliées à l'application lorsque celle-ci en a besoin. Les programmes qui les utilisent comportent les instructions nécessaires au chargement et à la liaison.

Dans la technique de la liaison dynamique utilisée par les DLL, l'opération de liaison est exécutée en deux temps : tout d'abord, au moment de la construction de l'application, l'éditeur de liens crée des connecteurs, puis au chargement ?#8364;” immédiatement avant l'exécution de l'application ?#8364;” les connecteurs sont reliés entre eux. Les DLL sont au format COFF, également utilisé dans IBM AIX.

Dans Windows

Les DLL sont une des fondations des systèmes d'exploitation Windows. Les fonctions internes de Windows sont mises à disposition des programmes par des interfaces de programmation mises en oeuvre par des fichiers DLL[10]. Les trois principales bibliothèques sont User32.dll (manipulation de l'interface utilisateur), GDI32.dll (manipulation des dispositifs d'impression et d'affichage), et Kernel32.dll (utilisation des fonctions du noyau de Windows concernant les fichiers et les processus). D'autres fichiers de Windows souvent utilisés en programmation sont comdlg32.dll, MAPI32.dll, netapi32.dll et winmm.dll. Les fichiers DLL sont également utilisés pour de nombreuses extensions : pilotes, boîtes de dialogue, widgets, polices de caractères.

Windows offre la possibilité de détourner ou de subtiliser les fonctions des DLL. Outre des raisons légitimes, ces possibilités sont exploitées par des rootkit, qui injectent des fonctionnalités à l'insu et contre le gré de l'utilisateur en subtilisant les DLL du système d'exploitation.

Programmation

Des langages comme par exemple C, C++ ou Delphi sont aptes à créer des DLL qui peuvent être exploitées par d'autres programmes. De nombreux outils de développement qui proposent des bibliothèques d'exécution à l'instar des MFC ou de la VCL de Borland proposent soit une liaison statique (intégration directe du code dans l'exécutable) soit une liaison dynamique (la bibliothèque est alors à distribuer sous forme de DLL).

L'utilisation de DLL permet de mettre à disposition du code et de rendre modulaire l'architecture d'une application. La mise à jour de celle-ci peut également se faire en remplaçant uniquement les DLL obsolètes. Néanmoins, l'utilisation de plusieurs versions concurrentes de DLL est problématique sous Windows et conduit à certaines incompatibilités regroupées sous le terme DLL Hell.

DLL hell - l'enfer des DLL

La construction des DLL permet de facilement les remplacer pour y ajouter des nouvelles fonctions et améliorer les fonctions existantes. Il arrive cependant que le remplacement entraîne des modifications en profondeur des fonctions existantes, ce qui entraîne des pannes et un fonctionnement erroné des programmes qui l'utilisent. C'est une cause connue d'appels aux services techniques qui ont baptisé ce phénomène DLL hell, « l'enfer des DLL » en anglais.

Lorsqu'un fichier DLL manque sur l'ordinateur de l'utilisateur, le lancement d'un programme qui s'en sert provoquera un message DLL Not found (« DLL non trouvée ») qui n'est d'aucune utilité pour l'utilisateur. Si la DLL existe mais n'est pas valable pour le programme, alors celui-ci n'a aucun moyen de le vérifier, ce qui peut entraîner des messages DynaLink error voire un crash du programme. Une des causes de tels incidents est que Windows n'offre pas de mécanisme interne de contrôle de version ?#8364;” mécanisme qui garantit qu'un programme sera associé avec la bibliothèque pour laquelle il a été construit.

Un des moyens qu'ont les programmeurs de lutter contre le DLL hell est de charger explicitement le fichier DLL : au lieu que le fichier soit automatiquement chargé au lancement du programme qui l'utilise, le programme utilise la fonction LoadLibrary, qui charge le fichier s'il existe.

Bibliographie

  • Hart, Johnson. Windows System Programming Third Edition. Addison-Wesley, 2005. ISBN 0-321-25619-0
  • Rector, Brent et al. Win32 Programming. Addison-Wesley Developers Press, 1997. ISBN 0-201-63492-9.

Liens externes

  • dllexport, dllimport sur MSDN
  • Dynamic-Link Libraries sur MSDN
  • What is a DLL? sur le site de Microsoft
  • Dynamic-Link Library Functions sur MSDN
  • Microsoft Portable Executable and Common Object File Format Specification
  • Microsoft specification for dll files
Composants de Microsoft Windows
Principal | Active Scripting (WSH · VBScript · JScript) · Aero · AutoPlay · AutoRun · ClearType · COM (ActiveX · COM Structured storage · DCOM · OLE · OLE Automation · Transaction Server) · Desktop Window Manager · DirectX · Explorer · Graphics Device Interface · Imaging Format · .NET Framework · Search (IFilters · Saved search) · Server Message Block · Shell (Extensions · Namespace · Dossier spécial) · Menu démarrer · Previous Versions · Taskbar · Windows USER · Console Win32 · XML Paper Specification
Configuration | Sauvegarder et restaurer · Cmd · Panneau de configuration · Device Manager · Nettoyage de disque · Défragmenteur de disque · Driver Verifier · Observateur d'événements · Console de gestion MMC·MSC · Netsh · Problem Reports and Solutions · Sysprep · System Policy Editor · System Configuration · Gestionnaire des tâches · System File Checker · Restauration du système · WMI · Windows Installer · Windows PowerShell · Windows Update · WAIK · WinSAT · Windows Easy Transfer
Applications | Calculatrice · Calendar · CD Player · Table de caractères · Contacts · Créateur de DVD Windows · Fax and Scan · Internet Explorer · Journal · Mail · Loupe · Media Center · Media Player · Meeting Space · Mobile Device Center · Mobility Center · Movie Maker · Narrator · Bloc-notes · Paint · Photo Gallery · Private Character Editor · Connexion Bureau à distance · Gadget du bureau · Outil Capture · Magnétophone · Reconnaissance vocale de Windows · Tablet PC Input Panel · WordPad
Jeux | Chess Titans · FreeCell · Hearts · Hold 'Em · InkBall · Mahjong Titans · Démineur · Pinball · Purble Place · Solitaire · Spider Solitaire · Tinker
Noyau | Ntoskrnl.exe · hal.dll · System Idle Process · Svchost.exe · Base de registre · Windows service · Service Control Manager · DLL · EXE · NTLDR / Boot Manager · Winlogon · Recovery Console · I/O · WinRE · WinPE · Kernel Patch Protection
Services | BITS · Planificateur de tâches · Wireless Zero Configuration · Shadow Copy · Error Reporting · Multimedia Class Scheduler · CLFS
Systèmes de fichiers | NTFS · REFS · EFS · WinFS · FAT32·FAT16·FAT12 · exFAT · CDFS · UDF · DFS · IFS
Serveur/Réseau | Domaine · Active Directory · DNS · Group Policy · Profil utilisateur · Folder redirection · Distributed Transaction Coordinator · MSMQ · Windows Media Services · Rights Management Services · IIS · Terminal Services · WSUS · Windows SharePoint Services · Network Access Protection · PWS · DFS Replication · Remote Differential Compression · Print Services for UNIX · Services d'installation à distance RIS · Windows Deployment Services · System Resource Manager · Hyper-V
Architecture | Architecture de Windows NT · Object Manager · Processus de démarrage de Windows NT · Processus de démarrage de Windows Vista/7 · I/O request packet · Kernel Transaction Manager · Logical Disk Manager · Security Account Manager · Windows File Protection / Windows Resource Protection · Windows library files · LSASS · CSRSS · SMSS · MinWin
Sécurité | User Account Control · BitLocker · Defender · Data Execution Prevention · Security Essentials · Protected Media Path · Mandatory Integrity Control · User Interface Privilege Isolation · Windows Firewall · Security Center
Compatibilité | Services for UNIX · Virtual DOS machine · command.com · Windows on Windows · WoW64 · Windows XP Mode

Liens


DDL Library

Dynamic-Link Libraries

In Microsoft® Windows®, dynamic linking provides a way for a process to call a function that is not part of its executable code. The executable code for the function is located in a dynamic-link library (DLL), containing one or more functions that are compiled, linked, and stored separately from the processes using them. The Microsoft® Win32® application programming interface (API) is implemented as a set of dynamic-link libraries, so any process using the Win32 API uses dynamic linking.

About Dynamic-Link Libraries

Dynamic linking allows an executable module (either a .DLL or .EXE file) to include only the information needed at run time to locate the executable code for a DLL function. This type of linking differs from the more familiar static linking, which requires a copy of a library function's executable code in the executable module of each application using it.

There are two methods for calling a function in a DLL:

· Load-time dynamic linking occurs when an application's code makes an explicit call to a DLL function. This type of linking requires that the executable module of the application be built by linking with the DLL's import library, which supplies the information needed to locate the DLL function when the application starts. For more information, see Load-Time Dynamic Linking_win32_Load_Time_Dynamic_Linking.

· Run-time dynamic linking occurs when a program uses the LoadLibrary_win32_LoadLibrary and GetProcAddress_win32_GetProcAddress functions to get the starting address of a DLL function. This type of linking eliminates the need to link with an import library. For more information, see Using Run-Time Dynamic Linking_win32_Using_Run_Time_Dynamic_Linking.

A DLL attaches to a process when the process starts or when one of its threads calls the LoadLibrary function. When a DLL attaches to a process, the operating system maps the DLL module into the address space of the process, making the DLL's executable code available to the process. When a DLL detaches from a process at process termination or as a result of a call to the FreeLibrary_win32_FreeLibrary function, the DLL module is unmapped from the address space of the process.

Like any other function, a DLL function is executed in the context of the thread (and the thread's process) that calls it. Therefore, the following conditions apply:

· The threads of the process that called the DLL can use handles opened by a DLL function. Similarly, handles opened by any thread of the calling process can be used in the DLL function.

· The DLL uses the user stack of the calling thread and the address space of the calling process.

· Memory allocated by the DLL is in the address space of the calling process.

· The threads of the process that called the DLL can read from or write to the globally declared variables in the DLL.

Advantages of Dynamic Linking

Dynamic linking has the following advantages:

· Many processes can use a single DLL simultaneously, sharing a single copy of the DLL in memory. Doing this saves memory space and reduces swapping.

· When the functions in a DLL change, the applications that use them do not need to be recompiled or relinked as long as the functions' arguments and return values do not change. In contrast, statically linked object code requires that the application be relinked when the functions change.

· A DLL can provide after-market support. For example, a display driver DLL can be modified to support a display that was not available when the application was shipped.

· Programs written in different programming languages can call the same DLL function as long as the programs follow the function's calling convention. The programs and the DLL function must be compatible in the following ways: the order the function expects its arguments to be pushed onto the stack, whether the function or the application is responsible for cleaning up the stack, and whether any arguments are passed in registers.

A potential disadvantage to using DLLs is that the application is not self-contained; it depends on the existence of a separate DLL module. A process using load-time dynamic linking is terminated by the operating system if the DLL is not found at process startup. A process using run-time dynamic linking is not terminated in this situation, but capabilities provided by the DLL are not available to the program.

Dynamic-Link Library Creation

To create a DLL, first create one or more source code (.C) files and a module-definition (.DEF) file. The .C file contains the exported functions for the DLL. The .DEF file lists these functions and defines other attributes of the DLL.

The compiler uses the .C file to create an object code (.OBJ) file. The library manager creates an export (.EXP) file and an import library (.LIB) file from the .DEF file.

Finally, the linker uses the .OBJ, .EXP, and .LIB files to build the executable files for the DLL. The .OBJ and .EXP files provide information the linker needs to build the .DLL file. The .LIB file contains information the linker needs to resolve external references to DLL functions, so that a program can locate the specified DLL at run time. The .LIB file enables the linker to build an executable module (.EXE or .DLL) that calls a function in the DLL. For more information about .DEF files, see Module-Definition File_win32_Module_Definition_File.

Note that the information in this section is based on using Win32 software development tools. For more detailed information about creating DLLs, see the documentation included with your development tools.

Dynamic-Link Library Entry-Point Function

A DllEntryPoint_win32_DllEntryPoint function is an optional DLL function that the operating system calls to perform user-defined operations. DllEntryPoint is not a typical function; it is a reference to a function specified in the linker command line. A DLL is not required to have an entry-point function, but it can be useful for initializing and cleaning up processes and threads. The name of a DLL's entry-point function is specified by the -entry switch of the linker. For example, the following command line specifies LibMain as the entry-point function of MYDLL.DLL.

link -entry:LibMain -out:mydll.dll mydll.exp mydll.obj $(mylibs)

If an entry-point function is specified, the operating system causes it to be called whenever any of the following events occurs:

· The DLL attaches to a process. For processes using load-time dynamic linking, attachment occurs during initialization. For processes using run-time linking, the DLL attaches and the entry-point function is called before LoadLibrary_win32_LoadLibrary returns.

· The DLL detaches from a process. Detachment occurs when the process terminates or when the DLL is unloaded by a call to the FreeLibrary_win32_FreeLibrary function. If the process terminates as a result of the TerminateProcess_win32_TerminateProcess or TerminateThread_win32_TerminateThread function, the system does not call the entry-point function.

· The DLL attaches to a thread. Attachment occurs whenever a new thread is created in a process to which a DLL is attached.

· The DLL detaches from a thread. Detachment occurs whenever a thread of an attached process terminates, except as a result of the TerminateThread or TerminateProcess function. When a DLL detaches from a process, the entry-point function is called only once for the entire process rather than once for each existing thread of the process.

The system causes the entry-point function to be called, but it is called in the context of a process or thread attaching to or detaching from the DLL. This allows a DLL to use its entry-point function for allocating memory in the address space of the process or to open handles accessible to the process. When it is called to attach a new thread, the entry-point function can allocate memory for thread local storage (TLS) that is private to the new thread. For more information about thread local storage, see Processes and Threads_win32_Processes_and_Threads.

When a DLL's entry-point function is called for process attachment, it returns TRUE to indicate success. For processes using load-time linking, a return value of FALSE causes the process initialization to fail and the process terminates. For processes using run-time linking, a return value of FALSE causes the LoadLibrary_win32_LoadLibrary function to return NULL, indicating failure. The return value of the entry-point function is disregarded when it is called for any other reasons, such as process detaching, thread attaching, or thread detaching.

Module-Definition File

A module-definition (.DEF) file is a text file containing one or more module statements that describe various attributes of a DLL. The descriptions and examples in this section are based on the keywords and syntax used by the Win32 software development tools. For complete information about module-definition files, see the documentation included with your development tools.

A DLL requires a .DEF file to create an import library (.LIB) file and an export (.EXP) file. The linker uses the import library to build any executable module that uses the DLL and uses the export file to build the .DLL file.

A DLL's module-definition file must include a LIBRARY statement and an EXPORTS statement. The LIBRARY statement identifies the module-definition file as belonging to a DLL, and it must be the first statement in the file. The name specified in the LIBRARY statement identifies the library in the DLL's import library. The EXPORTS statement lists the names and, optionally, the ordinal values of the functions exported by the DLL. When ordinal values are specified, they must be in the range 1 through N, where N is the number of functions exported by the DLL.

The following .DEF file for a DLL exports three functions and associates an ordinal value with each function.

LIBRARY mydll

EXPORTS

    Func_A @1

    Func_B @2

    Func_C @3

The .DEF file can contain additional statements, specifying other attributes of a DLL. For example, the SECTIONS statements can change the default scope of global and static variables. The following SECTIONS statement makes the DLL's initialized data shared, but its uninitialized variables are not shared.

SECTIONS

    .data READ WRITE SHARED    ; share initialized variables

Load-Time Dynamic Linking

Load-time dynamic linking occurs when an application's code explicitly calls a DLL function. When the source code is compiled or assembled, the DLL function call generates an external function reference in the object code. To resolve this external reference, the application must be linked with the import library for the DLL.

Finding an external function in an import library informs the linker that the code for that function is in a DLL. To resolve external references to DLLs, the linker simply adds information to the executable file that tells the system where to find the DLL code when the process starts up.

When the system starts a program that contains dynamically linked references, it uses the information in the program's executable file to locate the required DLLs. The system first searches the set of pre-installed DLLs, such as the performance library (KERNEL32.DLL) and the security library (USER32.DLL). The system then searches for the DLLs in the following sequence:

1. The directory where the executable module for the current process is located.

2. The current directory.

3. The windows system directory. The GetSystemDirectory_win32_GetSystemDirectory function retrieves the path of this directory.

4. The windows directory. The GetWindowsDirectory_win32_GetWindowsDirectory function retrieves the path of this directory.

5. The directories listed in the PATH environment variable.

Note that the LIBPATH environment variable is not used.

If it cannot locate a specified DLL, the system terminates the process and displays a dialog box that reports the error. Otherwise, the system maps the DLL modules into the process's address space.

If any of the DLLs has a DllEntryPoint_win32_DllEntryPoint function, the operating system calls the function. DllEntryPoint specifies a code that indicates the DLL is attaching to the process. If the entry-point function does not return TRUE, the system terminates the process and reports the error. For more information about the entry-point function, see Dynamic-Link Library Entry-Point Function_win32_Dynamic_Link_Library_Entry_Point_Function.

Finally, the system modifies the executable code of the process to provide starting addresses for the DLL functions.

Like the rest of a program's code, DLL code is mapped into the address space of the process when the process starts up and is loaded into memory only when needed. As a result, the PRELOAD and LOADONCALL code attributes used by .DEF files to control loading in previous versions of Windows no longer have meaning.

Run-Time Dynamic Linking

Run-time dynamic linking occurs when the process calls the LoadLibrary_win32_LoadLibrary function to specify the name of a DLL and the GetProcAddress_win32_GetProcAddress function to get the starting address of any function in the DLL. Run-time dynamic linking eliminates the need to link the process with an import library. Since the process does not explicitly call the DLL's functions, it does not generate external references to them.

If the call to LoadLibrary specifies a DLL module already mapped into the address space of the calling process, the function simply returns a handle of the DLL and increments the module's reference count. Otherwise, LoadLibrary attempts to locate the DLL using the same search sequence used for load-time dynamic linking (see Load-Time Dynamic Linking_win32_Load_Time_Dynamic_Linking). If the search succeeds, the system maps the DLL module into the address space of the process. If the DLL has a DllEntryPoint_win32_DllEntryPoint function, the operating system calls the function in the context of the thread that called LoadLibrary. The entry-point function is not called if the DLL is already attached to the process due to a previous call to LoadLibrary with no corresponding call to the FreeLibrary_win32_FreeLibrary function.

If the system cannot find the DLL or if the entry-point function returns FALSE, LoadLibrary returns NULL. If LoadLibrary succeeds, it returns a handle of the DLL module. The process can use this handle to identify the DLL in a call to the GetProcAddress or FreeLibrary function.

The GetModuleHandle_win32_GetModuleHandle function also returns a handle used in GetProcAddress or FreeLibrary. GetModuleHandle succeeds only if the DLL module is already mapped into the address space of the process by load-time linking or by a previous call to LoadLibrary. Unlike LoadLibrary, GetModuleHandle does not increment the module's reference count. The GetModuleFileName_win32_GetModuleFileName function retrieves the full path of the module associated with a handle returned by GetModuleHandle or LoadLibrary.

The process can use GetProcAddress_win32_GetProcAddress to get the starting handle returned by LoadLibrary_win32_LoadLibrary or the address of a function in the DLL using a DLL module handle returned by either LoadLibrary or GetModuleHandle. The function is specified by using its name or ordinal value as listed in the EXPORTS statement of the DLL's .DEF file.

When the DLL module is no longer needed, the process can call the FreeLibrary_win32_FreeLibrary function. This function decrements the module's reference count and unmaps it from the address space of the process if the reference count is zero.

Run-time dynamic linking enables the process to continue executing even if a DLL is not available. The process can then use an alternate method to accomplish its objective. For example, if a process is unable to locate one DLL, it can try to use another, or it can notify the user of an error. If the user can provide the full path of the missing DLL, the process can use this information to load the library even though it is not in the normal search path. This situation contrasts with load-time linking in which the operating system simply terminates the process if it cannot find the DLL.

It is not necessary for the developer to know the names of either the DLLs or the functions within a DLL. The process can get this information from a configuration file or from the user.

Run-time dynamic linking can cause problems if the DLL is using the DllEntryPoint_win32_DllEntryPoint function to perform initialization for each thread of a process, because threads existing when LoadLibrary_win32_LoadLibrary is called will not be initialized. For an example showing how to deal with this problem, see Using Thread Local Storage in a Dynamic-Link Library_win32_Using_Thread_Local_Storage_in_a_Dynamic_Link_Library.

Dynamic-Link Library Data

Windows supports the use of the following DLL data:

· Global and static variables in a DLL module private to each process that uses the DLL.

· Global and static variables shared by all processes simultaneously using a DLL.

· Dynamically allocated memory private to each process that uses a DLL.

· Dynamically allocated memory shared by multiple processes.

· Static storage private to each thread of a multithreaded process.

The default scope of a DLL's variables is the same as that of any statically linked code. Global variables in a DLL source code file are global to each process using the DLL. Static variables create permanent storage for each process, limiting their scope to the block in which they are declared. As a result, each process has its own instance of a DLL's global and static variables by default.

Win32 development tools enable a DLL's .DEF file to override the default scope of global and static variables. For more information, see Module-Definition File_win32_Module_Definition_File and the documentation included with your development tools.

When a DLL allocates memory using any of the allocation functions (GlobalAlloc_win32_GlobalAlloc, LocalAlloc_win32_LocalAlloc, HeapAlloc_win32_HeapAlloc, VirtualAlloc_win32_VirtualAlloc, malloc, and other C run-time allocation functions), the memory is allocated in the address space of the calling process and is accessible only to the threads of that process. A DLL's .DEF file cannot be used to change the scope of dynamically allocated memory.

A DLL can use file mapping to allocate memory that can be shared among processes. For a general discussion of using file mapping to create named shared memory, see File Mapping_win32_File_Mapping. For an example that uses the DllEntryPoint_win32_DllEntryPoint function to set up shared memory using file mapping, see Using Shared Memory in a Dynamic-Link Library_win32_Using_Shared_Memory_in_a_Dynamic_Link_Library.

The thread local storage functions enable a DLL to allocate an index for storing and retrieving a different value for each thread of a multithreaded process. For example, a spreadsheet application can create a new instance of the same thread each time the user opens a new spreadsheet. A DLL providing the functions for various spreadsheet operations can use thread local storage to save information about the current state of each spreadsheet (row, column, and so on). For a general discussion of thread local storage, see Processes and Threads_win32_Processes_and_Threads. For an example that uses the DllEntryPoint function to set up thread local storage, see Using Thread Local Storage in a Dynamic-Link Library_win32_Using_Thread_Local_Storage_in_a_Dynamic_Link_Library.

Using Dynamic-Link Libraries

· Creating a simple dynamic-link library_win32_Creating_a_Simple_Dynamic_Link_Library

· Using load-time dynamic linking_win32_Using_Load_Time_Dynamic_Linking

· Using run-time dynamic linking_win32_Using_Run_Time_Dynamic_Linking

· Using shared memory in a dynamic-link library_win32_Using_Shared_Memory_in_a_Dynamic_Link_Library

· Using thread local storage in a dynamic-link library_win32_Using_Thread_Local_Storage_in_a_Dynamic_Link_Library

Creating a Simple Dynamic-Link Library

DLLs make common functions available to a number of processes or threads at once. The following file, MYPUTS.C, contains a simple string printing function. This DLL does not use a DllEntryPoint_win32_DllEntryPoint function because it has no initialization or cleanup functions to perform.

#include <windows.h>

/*

 * A function that writes a null-terminated string to the

 * console output.

 */

VOID myPuts(LPTSTR lpszMsg) {

    DWORD cchWritten;

    HANDLE hStdout;

    /* Get a handle to the console output. */

    hStdout = GetStdHandle(STD_OUTPUT_HANDLE);

    /* Write a null-terminated string to the console output. */

    while (*lpszMsg)

        WriteFile(hStdout, lpszMsg++, 1, &cchWritten, NULL);

}

The module-definition file for this DLL is MYPUTS.DEF, and it contains the following information.

LIBRARY myputs

EXPORTS

     myPuts

The name specified in the LIBRARY statement identifies the DLL in the DLL's import library. Load-time dynamic linking enables the system to use this name for locating the DLL. If run-time dynamic linking is being used, the LoadLibrary_win32_LoadLibrary function does not use this name to locate the DLL. The EXPORTS statement indicates that other .EXE or .DLL modules can call the myPuts function.

The source code in MYPUTS.C is compiled to produce an object module (MYPUTS.OBJ). The library manager uses the module-definition file (MYPUTS.DEF) to create the import library (MYPUTS.LIB) and the export file (MYPUTS.EXP). The linker uses MYPUTS.EXP and MYPUTS.OBJ to produce the executable DLL module (MYPUTS.DLL). MYPUTS.LIB is not used in creating MYPUTS.DLL, but the linker uses it to create any executable module (.EXE or .DLL) that calls the myPuts function.

Following are some typical command lines used to compile and link a DLL. For more information about compiling and linking dynamic-link libraries and programs that use them, see the documentation included with your development tools.

cvars=-DWIN32 -D_MT -DSTRICT

# Compile myputs.c to create myputs.obj.

myputs.obj: myputs.c

    $(cc)  $(cflags) $(cvars) myputs.c

# Create the DLL's import library.

# Also create the myputs.exp export file.

myputs.lib: myputs.def

    $(lib) -machine:i386 \

    -def:myputs.def     \

    -out:myputs.lib

# Link myputs.obj and myputs.exp to create myputs.dll.

# myputs.lib is not used here but is included as a

# dependency to force creation of myputs.exp.

myputs.dll: myputs.obj myputs.lib

    $(link)          \

    -out:myputs.dll  \

    myputs.exp myputs.obj $(conlibs)

# If the DLL has a DllEntryPoint function, substitute a line

# like this to specify the name of the entry-point function.

#    $(link) -entry:LibMain    \

Using Load-Time Dynamic Linking

Once you have created a DLL, you can use it in an application. The following file, LOADTIME.C, is the source code for a simple console process that uses the myPuts function from MYPUTS.DLL.

/*

 *   File:  loadtime.c

 *   A simple program that uses myPuts() from myputs.dll

 */

#include <windows.h>

VOID myPuts(LPTSTR);   /* a function from a DLL */

VOID main(VOID) {

    myPuts("message printed using the DLL function\n");

}

Because LOADTIME.C calls the DLL function explicitly, the executable module for the application must be linked with the import library (MYPUTS.LIB). Following are some typical command lines used for compiling and linking a program that uses a DLL.

# Compile loadtime.c to create loadtime.obj.

loadtime.obj: loadtime.c

    $(cc)  $(cflags) $(cvars) loadtime.c

# Link loadtime.obj and myputs.lib to create loadtime.exe.

loadtime.exe: loadtime.obj myputs.lib

    $(link) $(conflags) \

    -out:loadtime.exe loadtime.obj myputs.lib $(conlibs)

Using Run-Time Dynamic Linking

You can use the same DLL in both load-time and run-time dynamic linking. If the system can find the specified DLL module, the following source code produces the same output as the load-time example in the previous section. The program uses the LoadLibrary_win32_LoadLibrary function to get a handle of MYPUTS.DLL. If LoadLibrary succeeds, the program uses the returned handle in the GetProcAddress_win32_GetProcAddress function to get the starting address of the DLL's myPuts function. After calling the DLL function, the program calls the FreeLibrary_win32_FreeLibrary function to detach the DLL from the process.

The following example illustrates an important difference between run-time and load-time dynamic linking. If the MYPUTS.DLL file is not available, the application using load-time dynamic linking simply terminates. The run-time dynamic linking example, however, can respond to the error.

/*

 *   File:  runtime.c

 *   A simple program that uses LoadLibrary and

 *   GetProcAddress to access myPuts() from the myputs DLL.

 */

#include <stdio.h>

#include <windows.h>

typedef VOID (*MYPROC)(LPTSTR);

VOID main(VOID) {

    HINSTANCE hinstLib;

    MYPROC ProcAdd;

    BOOL fFreeResult, fRunTimeLinkSuccess = FALSE;

    /* Get a handle to the DLL module. */

    hinstLib = LoadLibrary("myputs");

    /* If the handle is valid, try to get the function address. */

    if (hinstLib != NULL) {

        ProcAdd = (MYPROC) GetProcAddress(hinstLib, "myPuts");

        /* If the function address is valid, call the function. */

        if (fRunTimeLinkSuccess = (ProcAdd != NULL))

            (ProcAdd) ("message via DLL function\n");

        /* Free the DLL module. */

        fFreeResult = FreeLibrary(hinstLib);

  }

    /* If unable to call the DLL function, use an alternative. */

    if (! fRunTimeLinkSuccess)

        printf("message via alternative method\n");

}

Because the program uses run-time dynamic linking, it is not necessary to link with the DLL's import library when creating the program's executable module. The following lines are typical of the command lines used to compile and link a program using run-time linking.

# Compile runtime.c to create runtime.obj.

runtime.obj: runtime.c

    $(cc)  $(cflags) $(cvars) runtime.c

# Link runtime.obj to create loadtime.exe.

runtime.exe: runtime.obj

    $(link) $(conflags) \

    -out:runtime.exe runtime.obj $(conlibs)

Using Shared Memory in a Dynamic-Link Library

This section shows the use of a DLL's entry-point function to set up memory that can be shared by multiple processes. In this DLL, the name of the entry-point function is LibMain. The module-definition file for this DLL is MYPUTS.DEF, and it contains the following information.

LIBRARY myputs

EXPORTS

     myPuts

The example uses file mapping to map a block of named shared memory into the address space of each process to which the DLL is attached. The entry-point function calls the CreateFileMapping_win32_CreateFileMapping function to create a file-mapping object whenever the DLL attaches to a new process. This function can be used to either create or open a named file-mapping object. The first process to which the DLL attaches creates the file-mapping object. Subsequent processes open a handle of the existing object. The GetLastError_win32_GetLastError function triggers initialization of the shared memory because it returns NO_ERROR to the first process and ERROR_ALREADY_EXISTS to subsequent processes.

The MapViewOfFile_win32_MapViewOfFile function maps the file-mapping object into the address space of each process, returning a pointer to the memory. Note that the shared memory can be mapped to a different address in each process. For this reason, each process has its own instance of lpvMem, which is declared as a global variable so that it is available to the DLL's functions. The example assumes that the DLL's global data is not shared, so each attaching process has its own instance of lpvMem. By default, DLL data is not shared, but the default can be modified by the DLL's module-definition (.DEF) file (see Module-Definition File_win32_Module_Definition_File).

Any shared DLL memory persists only as long as the DLL is attached to a process. In this example, the shared memory is released when the last handle of the file-mapping object is closed. To create persistent shared memory, a DLL can create a detached process (see Processes and Threads_win32_Processes_and_Threads) when the DLL attaches to the first process. If this detached process uses the DLL and does not terminate, it has a handle of the file-mapping object that prevents the shared memory from being released.

/*

 *   File:  dllshmem.c

 *

 *   A DLL entry-point function sets up shared memory using

 *   named file mapping.

 */

#include <windows.h>

#include <memory.h>

#define SHMEMSIZE 4096

static LPVOID lpvMem = NULL; /* address of shared memory */

/*

 * LibMain() is the DllEntryPoint function for this DLL.

 */

BOOL LibMain(HINSTANCE hinstDLL,   /* DLL module handle  */

        DWORD fdwReason,      /* reason called           */

        LPVOID lpvReserved) { /* reserved                */

    HANDLE hMapObject = NULL;   /* handle to file mapping */

    BOOL fInit, fIgnore;

    switch (fdwReason) {

        /*

         * The DLL is attaching to a process due to process

         * initialization or a call to LoadLibrary.

         */

          case DLL_PROCESS_ATTACH:

            /* Create a named file mapping object */

            hMapObject = CreateFileMapping(

                (HANDLE) 0xFFFFFFFF, /* use paging file    */

                NULL,                /* no security attr.  */

                PAGE_READWRITE,      /* read/write access  */

                0,                   /* size: high 32-bits */

                SHMEMSIZE,           /* size: low 32-bits  */

                "dllmemfilemap");    /* name of map object */

            if (hMapObject == NULL)

                return FALSE;

            /* The first process to attach initializes memory. */

            fInit = (GetLastError() != ERROR_ALREADY_EXISTS);

            /* Get a pointer to the file-mapped shared memory. */

            lpvMem = MapViewOfFile(

                hMapObject,     /* object to map view of    */

                FILE_MAP_WRITE, /* read/write access        */

                0,              /* high offset:   map from  */

                0,              /* low offset:    beginning */

                0);             /* default: map entire file */

            if (lpvMem == NULL)

                return FALSE;

            /* Initialize memory if this is the first process. */

            if (fInit)

                memset(lpvMem, '\0', SHMEMSIZE);

            break;

        /* The attached process creates a new thread. */

        case DLL_THREAD_ATTACH:

            break;

        /* The thread of the attached process terminates. */

        case DLL_THREAD_DETACH:

            break;

        /*

         * The DLL is detaching from a process due to

         * process termination or a call to FreeLibrary.

         */

        case DLL_PROCESS_DETACH:

            /* Unmap shared memory from the process's address space. */

            fIgnore = UnmapViewOfFile(lpvMem);

            /* Close the process's handle to the file-mapping object. */

            fIgnore = CloseHandle(hMapObject);

            break;

        default:

          break;

  }

    return TRUE;

    UNREFERENCED_PARAMETER(hinstDLL);

    UNREFERENCED_PARAMETER(lpvReserved);

}

/*

 * SetSharedMem sets the contents of shared memory.

 */

VOID SetSharedMem(LPTSTR lpszBuf) {

    LPTSTR lpszTmp;

    /* Get the address of the shared memory block. */

    lpszTmp = (LPTSTR) lpvMem;

    /* Copy the null-terminated string into shared memory. */

    while (*lpszBuf)

        *lpszTmp++ = *lpszBuf++;

    *lpszTmp = '\0';

}

/*

 * GetSharedMem gets the contents of shared memory.

 */

VOID GetSharedMem(LPTSTR lpszBuf, DWORD cchSize) {

    LPTSTR lpszTmp;

    /* Get the address of the shared memory block. */

    lpszTmp = (LPTSTR) lpvMem;

    /* Copy from shared memory into the caller's buffer. */

    while (*lpszTmp && --cchSize)

        *lpszBuf++ = *lpszTmp++;

    *lpszBuf = '\0';

}

Using Thread Local Storage in a Dynamic-Link Library

This section shows the use of a DLL's entry-point function to set up a thread local storage (TLS) index to provide private storage for each thread of a multithreaded process.

The entry-point function uses the TlsAlloc_win32_TlsAlloc function to allocate a TLS index whenever the DLL attaches to a process. Each thread of the process can then use this index to store a pointer to its own block of memory.

When the entry-point function is called with the DLL_PROCESS_ATTACH value, it peforms the following actions:

1. Uses the TlsAlloc function to allocate a TLS index.

2. Allocates a block of memory to be used exclusively by the initial thread of the process.

3. Uses the TLS index in a call to the TlsSetValue_win32_TlsSetValue function to store a pointer to the allocated memory.

Each time the process creates a new thread, the entry-point function is called with the DLL_THREAD_ATTACH value. The entry-point function then allocates a block of memory for the new thread and stores a pointer to it by using the TLS index. Each thread can use the TLS index in a call to TlsGetValue_win32_TlsGetValue to retrieve the pointer to its own block of memory.

When a thread terminates, the entry-point function is called with the DLL_THREAD_DETACH value and the memory for that thread is freed. When a process terminates, the entry-point function is called with the DLL_PROCESS_DETACH value and the TLS index is freed.

The TLS index is stored in a global variable, making it available to all of the DLL's functions. The following example assumes that the DLL's global data is not shared, because the TLS index is not necessarily the same for each process to which the DLL is attached.

static DWORD dwTlsIndex; /* address of shared memory */

/*

 * LibMain() is the DllEntryPoint function for this DLL.

 */

BOOL LibMain(HINSTANCE hinstDLL,   /* DLL module handle  */

        DWORD fdwReason,     /* reason called      */

        LPVOID lpvReserved) {/* reserved           */

    LPVOID lpvData;

    BOOL fIgnore;

    switch (fdwReason) {

        /*

         * The DLL is attaching to a process due to process

         * initialization or a call to LoadLibrary.

         */

        case DLL_PROCESS_ATTACH:

              /* Allocate a TLS index. */

              if ((dwTlsIndex = TlsAlloc()) == 0xFFFFFFFF)

                  return FALSE;

            /* No break: Initialize the index for first thread. */

        /* The attached process creates a new thread. */

        case DLL_THREAD_ATTACH:

            /* Initialize the TLS index for this thread. */

            lpvData = (LPVOID) LocalAlloc(LPTR, 256);

            if (lpvData != NULL)

                fIgnore = TlsSetValue(dwTlsIndex, lpvData);

            break;

        /* The thread of the attached process terminates. */

        case DLL_THREAD_DETACH:

            /* Release the allocated memory for this thread. */

            lpvData = TlsGetValue(dwTlsIndex);

            if (lpvData != NULL)

                LocalFree((HLOCAL) lpvData);

            break;

        /*

         * The DLL is detaching from a process due to

         * process termination or call to FreeLibrary.

         */

        case DLL_PROCESS_DETACH:

            /* Release the allocated memory for this thread. */

            lpvData = TlsGetValue(dwTlsIndex);

            if (lpvData != NULL)

                LocalFree((HLOCAL) lpvData);

            /* Release the TLS index. */

            TlsFree(dwTlsIndex);

            break;

        default:

            break;

    }

    return TRUE;

    UNREFERENCED_PARAMETER(hinstDLL);

    UNREFERENCED_PARAMETER(lpvReserved);

}

When a process uses load-time linking with this DLL, the entry-point function is sufficient to manage the thread local storage. Problems can occur with a process that uses run-time linking because the TLS memory is not allocated for threads that exist before the LoadLibrary_win32_LoadLibrary function is called to attach the process. The following example solves this problem by checking the value returned by the TlsGetValue_win32_TlsGetValue function and allocating memory if the value indicates that the TLS slot for this thread is not set.

LPVOID lpvData;

/* Retrieve a data pointer for the current thread. */

lpvData = TlsGetValue(dwTlsIndex);

/* If NULL, allocate memory for this thread. */

if (lpvData == NULL) {

    lpvData = (LPVOID) LocalAlloc(LPTR, 256);

    if (lpvData != NULL)

        TlsSetValue(dwTlsIndex, lpvData);

}

Dynamic-Link Library Functions

Following are the functions used in dynamic linking.

DisableThreadLibraryCalls _win32_DisableThreadLibraryCalls
DllEntryPoint_win32_DllEntryPoint
FreeLibrary_win32_FreeLibrary
FreeLibraryAndExitThread_win32_FreeLibraryAndExitThread
GetModuleFileName_win32_GetModuleFileName
GetModuleHandle_win32_GetModuleHandle
GetProcAddress_win32_GetProcAddress
LoadLibrary_win32_LoadLibrary
LoadLibraryEx_win32_LoadLibraryEx
LoadModule_win32_LoadModule

Obsolete or Deleted Functions

FreeModule_win32_FreeModule
FreeProcInstance_win32_FreeProcInstance
GetCodeHandle_win32_GetCodeHandle
GetInstanceData_win32_GetInstanceData
GetModuleUsage_win32_GetModuleUsage
MakeProcInstance_win32_MakeProcInstance


Dynamic-link libraries tests

 "Use of Win32" demonstrations
Dynamic-link Libraries (Dll) tests
What Dlls are
Why Dlls are useful
Dll loading, memory, stack and threads
Calling with no arguments
Calling with arguments via the stack
Start-up code and run-time link
Dll sending data to the calling exe
Exporting data pointers
Dll using function in the calling exe
Calling a function by ordinal using GetProcAddress
Calling a function directly by ordinal
Calling a function by name-load
Testing who called start-up code

Everyone knows that you can call a function in a Dll. You can also pass data to the function and receive data from the function on the stack or in the registers. But until I wrote my linker GoLink, it was difficult to try to do more than that. In previous versions of this help file I described these problems in detail, but now assembler programmers have achieved their independence there is no need to dwell on the past. Instead I examine why Dlls are useful, and the various things you can do with them.

What Dlls are
In a Win32 context, Dlls are executable files in Portable Executable format which have the value 2000h in the Characteristics flag (at +16h the file header). An ordinary executable file (an "Exe") has the value 2h. A Dll also normally has the extension ".Dll", although this is not obligatory. For example, ".ocx" files work the same way. The flag is inserted at link-time by the linker. To achieve this, you tell the linker you want to make a Dll rather than an Exe. When the Win32 system loads an Exe, any Dlls holding the imports required by the Exe will also be loaded. These are usually imported functions which can be called by the Exe but as we shall see it is also possible to import data addresses. The system loader knows what imports are required by the Exe and which Dll contains those imports because these are listed in the idata section of the Exe. This information is put in the Exe at link-time by the linker. The linker (GoLink anyway) gets the information it needs about which Dll holds which function, from a list of Dlls (or other executables) the user provides at link time.

Other linkers need a list of library ("Lib") files. A Lib file is usually created from the Dll and is a list of exports from the Dll. This usually done by a utility at compile-time although sometimes it is done by the linker itself. In the case of system Dlls, the Lib files are issued by Microsoft, but they can also be made by the programmer by passing the Dll to the correct utility. Lib files are still around because they can also contain other information about functions, but if you use GoLink this is no longer required.

Why Dlls are useful
Dlls permit sharing of functions. In Win32, the system Dlls contain the APIs (Application Programmers' Interface). These are a vast array of functions which are the programmer's access to all the functions available in Win32. This is a much more flexible system than the old DOS interrupt system, since each Dll can be updated when necessary, without having to update the operating system as a whole.
Using Dlls makes it easier and quicker to update parts of code or data in a particular application which may need regular updating. You just update the Dll not the main Exe.
Dlls save memory by file mapping. Since Win32 is a multi-tasking operating system, it is possible to have several applications running and sharing the code and data in the Dlls at the same time. But at any one time only one copy of the Dll will actually be in physical memory (see below).
Using a Dll it is possible to spy on other programs. This can be done by loading the Dll in the other program's memory space (as in my debugger's GoBugSpy.Dll).
Using run-time linking of a Dll it is possible to run an Exe even if certain functions are not available. The most likely reason such functions might not be available is if the user is running the program under an early version of the operating system. In a properly designed program this ought not to be fatal, but mean merely that certain program embellishments cannot be used. If the Dll cannot be loaded the user can be informed that a particular function cannot be performed because the operating system is out of date!

Dll loading, memory, stack and threads
We have seen that at load-time any required Dlls will also be loaded. This means that the Dll file will be read and loaded into physical memory, ready for execution. However, the Dll may already be in physical memory if another running application has loaded it. This often happens in the case of system Dlls. In that case instead of loading another copy of the Dll into physical memory the system maps the file and supplies the Exe with the correct addresses for the functions it needs in the Dll. These addresses are supplied as virtual addresses. When the Exe calls the function, it calls the virtual address, but in reality this accesses the Dll in physical memory. This is possible because the processor works in "protected" mode. This means that all addresses are virtual addresses rather than actual physical addresses. The system converts the virtual address into a real address. All this is controlled by the system and the programmer need not worry about it.
The use of virtual addresses means that each Exe can be run in its own address (or memory) space. This adds greatly to robustness in Win32 because it means that running programs cannot interfere with each other's memory areas. The non-system Dlls used by the Exe are also loaded in the address space of the Exe itself. However, in Windows 95, 98 and ME system Dlls are loaded into shared memory, which is above 7FFFFFFFh. This area of memory is "shared" in the sense that the same address space is available to all running programs and all addresses in the shared area will therefore be the same. It is different in Windows NT, 2000 and XP. In those versions of the operating system even addresses in shared memory are unique so that system Dlls are in their own address space. In theory system Dlls could have different addresses for different programs, although in practice they normally don't. Normally the system will not allow a program to write to shared memory, so even in Win95, 98 and ME it is difficult to corrupt the Dlls which are loaded in shared memory.
The significance of all this is that an Exe can regard code in its Dlls as effectively its own code. It may call the function required in the normal way even though the function resides in the Dll (see no arguments).
It also means that the Dll can access, read or modify all data-in-memory stored by the Exe. However, in order to do this, the Dll will need to know the address of the data (see below).
The Dll shares the same stack as the Exe. So when calling a function in a Dll it is possible to send data or data addresses to the function on the stack (see arguments via the stack), or to receive them via the stack.
A Dll also shares the same thread as the Exe. When an Exe calls the Dll it is the same thread which runs in the Dll. This means that the registers are exactly the same registers as the Exe is using. Therefore data or data addresses can be sent to the Dll in the registers, or received from the Dll in registers (see exporting data indirectly to Testbug).
An Exe or Dll can export a data pointer to another executable. This is easy to do using GoAsm and GoLink - see exporting data pointers.
Just as an Exe can regard a Dll's code as its own, so a Dll may regard the Exe's code as its own, which it can use when it needs to. Whereas this used to be difficult, using GoAsm and GoLink it is easy - see using function in Testbug.exe itself.

Calling with no arguments
This demonstrates a simple call by the Exe of a function contained in the Dll. When watching with a debugger, use the breakpoint DLL_TEST1A. When the function DLL_TEST1 is called, note that the thread handle does not change. Note the address of the code before and after the call to the Dll. After the call the code is in a higher address in memory where the Dll has been loaded. Note the values of ESP and EBP which change to suit the call only, showing that the same stack is being used. Note that the thread is the same thread as the caller. Here is the code used in the Dll:-

DLL_TEST1:
PUSH 40h                ;information+ok button only
PUSH 'Testbug - Dll test (no arguments)'
PUSH ADDR DLLMESS1
PUSH 0
CALL MessageBoxA        ;wait till ok pressed
RET

Calling with arguments via the stack
Here 3 arguments (or parameters) are sent to the Dll by the caller on the stack. When watching with a debugger, use the breakpoint DLL_TEST2A. It's important to know how to send data to and receive data from a Dll since as we shall see later, only data pointers can be exported to and imported from executables. Here putting the data on the stack is simple. It is just PUSHed before the call is made. Each PUSH causes the ESP register to reduce by 4 bytes and you have to make sure that it is restored to the original value when returning to the caller's code. If this is not done, previous calls will not return to the correct code place, local data may not be addressed properly and stack memory will be eroded due to a "memory leak". If data is pushed on the stack before the call in this way you can either restore ESP by using POP (in this case 3 times), or by increasing ESP by 12 bytes after the call has returned, or as in this case, using RET 0Ch in the called procedure itself, which does the same thing. In fact, this is exactly how the system functions deal with data PUSHed on the stack.
Here is the code in Testbug.Exe which provides the arguments and sets up the call:-

DLL_TEST2A:
PUSH [hWnd]
PUSH 'Pointers to the title and this message, and the hWnd have been passed on the stack'
PUSH 'Dll test number 2'
CALL DLL_TEST2B         ;this function is in TESTBUG1.DLL
XOR EAX,EAX
RET
And this is the code in the Dll. Note that the EBP register is used within a "stack frame" to address the arguments passed on the stack. This is normally a matter of convenience, since it allows the arguments to be addressed and can also local data to be held on the stack, all addressed using EBP without any need to worry about ESP. If you want to use the stack to hold local data subtract the correct number of bytes from ESP after giving it to EBP. Then the local data can be addressed using EBP minus an amount, and the parameters can be addressed by using EBP plus an amount.
DLL_TEST2B:
PUSH EBP                ;receiving 3 parameters from caller
MOV EBP,ESP
;now [EBP+8]=3rd param, [EBP+0Ch]=2nd param, [EBP+10h]=1st param
PUSH 40h                ;information+ok button only
PUSH [EBP+8]            ;title
PUSH [EBP+0Ch]          ;message
PUSH [EBP+10h]          ;hWnd to use
CALL MessageBoxA        ;wait till ok pressed
MOV ESP,EBP
POP EBP
RET 0Ch

Start-up code and run-time link
There are two demonstrations here. The first is Dll start-up and close-down code. The second is run-time linking.
When loading any Dll, the system looks for the starting address. This value is contained in the PE file, in the same way as the starting address of an Exe is contained in the PE file. When the system loads the Dll it calls the start-up code. The function at that address must return EAX=1 otherwise loading is aborted. However this is the opportunity for the Dll to arrange its own affairs. The most useful thing it can do is to set up a memory area of its own in which to keep its own data. When the Exe is ready it can call the Dll and ask for the address of this memory area so that it can access it. In the demonstration here, the Dll simply shows a message box with the correct message. On closure, the Dll starting address will again be called, but with a different flag set in one of the arguments. This is the opportunity for the Dll to clear up, for example it may close the memory that it made earlier.
It is not essential to provide start-up and close-down code in Dlls, and it can be safely omitted.
Run-time linking.
All Dlls known to contain functions used by the Exe are loaded when the Exe is loaded. The normal way to call such functions are to call them by name. This causes the linker at compile time to mark the function as an import causing the system to load the Dll when the Exe is loaded. However there is another way to call a function in a Dll (or another Exe for that matter). This is by calling the function directly using the API GetProcAddress. Sometimes this is a real advantage because if a function in a Dll is missing at load time the system will not load the Exe and it cannot be run at all. If there is any chance that this could happen, for example if the function is only available in later versions of the operating system, then instead of calling the API by name, it is much better to call the API using GetProcAddress. If GetProcAddress returns with an error because the API cannot be found, then your program can find some other way to achieve the task or gracefully drop out of the task. GetProcAddress must be given the handle to the Dll. In practice, this is simply the virtual address in the program's memory space where the Dll is loaded. Of course, your program must ensure that the Dll is loaded (if there are no functions called-by-name it will not have been loaded at load-time). This is done by the API LoadLibrary. If LoadLibrary finds that the Dll is loaded already, it does not try to load the Dll again, but simply increments the load count. FreeLibrary decrements the count, and unloads the Dll if it is then zero. It would be possible for the Exe to load all required Dlls itself on start-up keeping all returned handles (LoadLibrary returns the handle in EAX). This would avoid having to call LoadLibrary each time just before GetProcAddress and FreeLibrary just after.
When watching this code with a debugger, use the breakpoint DLL_TEST3A in Testbug.Exe to view the caller's code. Then single-step/jump over the API call LoadLibrary. Note that the Dll's start-up code will be executed before the return from LoadLibrary (you can see this because the message box made in the start-up code will appear). The LoadLibrary code calls the Dll's start-up code, loads the Dll and then returns. Then the function in the Dll. You can see that happening because another message box is called. Finally a third message box appears and this happens in the call to FreeLibrary. Each of the message boxes you see also show the value of FS:[18h]. This is the address in memory of the Thread Environment Block (TEB) which is a thread-specific value [in Windows 9x and ME this is called the Thread Information Block (TIB)]. This demonstrates, therefore, which thread is calling the Dll at start up, which thread is calling the function and which thread is calling the Dll when it is freed. In fact you can see that it is the same thread in each case. It is not a special system thread. What is happening is that the system is using the application's own thread to call the Dlls start-up and unload code. This is obviously sensible to avoid system conflicts. See test 10 which checks which thread calls the start-up code when the Dll is loaded at the same time as the main Exe. Have a guess - would it be the same thread as the main Exe, or would it be a system thread?
Here is the caller's code in Testbug.Exe; one interesting thing about this code is that the API FreeLibrary needs one argument (the handle to the dll returned by LoadLibrary), but in assembler it is possible to PUSH this after the call to LoadLibrary if successful rather than immediately before the call to FreeLibrary itself:-

DLL_TEST3A:
PUSH 'TESTBUG3.DLL'
CALL LoadLibraryA       ;load the dll and return handle in eax
OR EAX,EAX              ;see if worked
JZ >L825                ;no
PUSH EAX                ;save the handle for later
;************
PUSH 'DLL_TEST3B'       ;this function is in TESTBUG3.DLL
PUSH EAX                ;DLL handle
CALL GetProcAddress
OR EAX,EAX
JZ >L824
CALL EAX                ;call the correct address
L824:
CALL FreeLibrary        ;free the dll (handle pushed earlier)
L825:
XOR EAX,EAX
RET
And here is the code in the Dll. Firstly the start-up and close-down code (Code symbol MAIN), then the actual function called by Testbug.Exe (DLL_MESS3B) followed by HEXROTATE8 which writes a 32-bit hex number to [EDI].
Note: it is important always to save the registers EBX,EDI,and ESI if they are used.
MAIN:                   ;starting address
PUSH EBP                ;receiving 3 parameters from caller
MOV EBP,ESP
PUSH EBX,EDI,ESI
;now [EBP+8]=hInstDLL, [EBP+0Ch]=fdwReason, [EBP+10h]=reserved
MOV ESI,ADDR DLLMESS1
MOV EDI,ADDR M1
CMP D[EBP+0Ch],1        ;see if DLL_PROCESS_ATTACH
JZ >L200                ;yes
MOV EDI,ADDR M3
MOV ESI,ADDRR DLLMESS3
JNB >L202               ;b=DLL_PROCESS_DETACH
L200:
FS MOV EAX,[18h]
CALL HEXROTATE8         ;write hex number from eax into edi
PUSH 40h                ;information+ok button only
PUSH 'Testbug - Example of run-time dynamic linking and start-up code'
PUSH ESI
PUSH 0
CALL MessageBoxA        ;wait till ok pressed
L202:
MOV EAX,1               ;return true to continue loading/action
POP ESI,EDI,EBX
MOV ESP,EBP
POP EBP
RET 0Ch
;
DLL_TEST3B:
MOV EDI,ADDR M2
FS MOV EAX,[18h]
CALL HEXROTATE8         ;write hex number from eax into edi
PUSH 40h                ;information+ok button only
PUSH 'Testbug - Example of run-time dynamic linking'
PUSH ADDR DLLMESS2
PUSH 0
CALL MessageBoxA        ;wait till ok pressed
RET
;
HEXROTATE8:             ;write hex number from eax into edi
MOV ECX,8
L200:
ROL AX,4                ;get high order nibble into al
MOV DL,AL
AND DL,0Fh              ;use only least sig nibble
ADD DL,48D              ;convert to ascii char
CMP DL,57D
JNA >L201
ADD DL,7                ;write hex letter if necessary A=65D
L201:
MOV [EDI],DL            ;write the nibble
INC EDI                 ;ready for next
LOOP L200
RET

Dll sending data to the calling exe
This is a simple test showing the use of registers to send data from a Dll. This is possible because the thread used by the Dll is the same as that which calls the Dll so the registers are not changed when the call occurs or when execution returns to the caller. In this demonstration the function in the Dll passes a data symbol DLLMESS4 via EAX back to the caller. Obviously you could do this the other way too, and use a register or registers to pass data to the Dll.
Here is the code in Testbug.Exe:-

DLL_TEST4A:
CALL DLL_TEST4B         ;this function is in TESTBUG1.DLL
PUSH 40h                ;information+ok button only
PUSH 'Testbug - Exported text from TESTBUG1.DLL via call'
PUSH EAX                ;address of text passed via EAX by TESTBUG1.DLL
PUSH [hWnd]
CALL MessageBoxA        ;wait till ok pressed
XOR EAX,EAX
RET
And here is the code in the Dll:-
DLL_TEST4B:             ;symbol export
PUSH 40h                ;information+ok button only
PUSH 'Testbug - Dll test - symbol export'
PUSH ADDR DLLMESS3
PUSH 0
CALL MessageBoxA        ;wait till ok pressed
MOV EAX,OFFSET DLLMESS4
RET

Exporting data pointers
Here is another way of exporting data using an Exe or a Dll. Prior to GoLink, this was quite difficult for assembler programmers to achieve. The data cannot be exported directly, instead you have to send a pointer to the data and then the pointer is used to get the data. What is being sent is only the address of the data in the same way as the address of a function can be sent. This enables an Exe and a Dll to share data easily. Testbug2.Dll is used in this example.

First you need to declare the data in the Dll in the usual way in the data section (in GoAsm) preceded by the word EXPORT:-

EXPORT EXPORTED_SYMBOL DD 0DAFFC0FFh
Then in the main Exe you can use the data declared in the Dll as follows (use the breakpoint DLL_TEST5 to view this in action):-
DLL_TEST5:
MOV EDI,ADDR DLLMESS10
ADD EDI,78D             ;get to place for value
MOV EAX,[EXPORTED_SYMBOL]  ;variable declared in TESTBUG2.DLL
MOV EAX,[EAX]           ;you only get its offset, not its value
CALL HEXWRITE
PUSH 40h                ;information+ok button only
PUSH 'Testbug - Data symbol exported from TESTBUG2.DLL'
PUSH ADDR DLLMESS10
PUSH [hWnd]
CALL MessageBoxA        ;wait till ok pressed
XOR EAX,EAX
RET

Dll using function in the calling exe
There will be many standardised functions which will be called frequently by your code. These may reside in a Dll, but why should they? It is possible to have them in the main Exe. Then if the Dll needs to use them it will call the main Exe.
Exporting a function either from an exe file or a Dll, can be achieved in GoAsm by using the word EXPORT just before the function to be exported:-

EXPORT HEXWRITE:
;function code goes here
Alternatively in GoAsm you can declare the EXPORT if no section has yet been declared eg.
EXPORT HEXWRITE
The only remaining thing to do is to tell GoLink to look in Testbug.exe for exports when making the Dll. This is achieved by including Testbug.exe in GoLink's command line or command file. Note this does nor work with other linkers.
To see the export HEXWRITE in Testbug.exe in action set a breakpoint at DLL_TEST6 in Testbug1.Dll and run Dll Test 6. This calls the function to write a hex number to a message box.

Calling a function by ordinal using GetProcAddress
A function in a Dll does not have to be called by name. It is possible to call it by number (known as "ordinal"). The only advantage of this is that the program may load a little quicker because with functions called by name, at start-up the loader has to find all the addresses of the functions and write them into the Exe as loaded in memory. It does not do this with functions loaded by ordinal. One way to call a function by ordinal is by loading its address using GetProcAddress. This API must be given the handle to the appropriate Dll so LoadLibrary has to be called at some point to get this value, and its probably a good idea to call FreeLibrary on closure of the program.
To see this in action, set a breakpoint on DLL_TEST7 (in Testbug.Exe).
You must fix the value of the ordinal for the function, and you do this (using GoAsm) by including this line in the assembler source script (this makes the DLL_TEST7B function an export with an ordinal of 10) :-

EXPORTS DLL_TEST7B:10
Here is the code to make the call-by-ordinal using GetProcAddress:-
DLL_TEST7A:
PUSH 'TESTBUG1.DLL'
CALL LoadLibraryA       ;load the dll and return handle in eax
OR EAX,EAX              ;see if worked
JZ >L4                  ;no
PUSH EAX                ;save the handle for later
;
PUSH 10D                ;this function is in TESTBUG1.DLL, called by ordinal
PUSH EAX                ;DLL handle
CALL GetProcAddress
OR EAX,EAX
JZ >L3
CALL EAX                ;call the correct address
L3:
CALL FreeLibrary        ;free the dll (handle pushed earlier)
L4:
XOR EAX,EAX
RET
And here is the actual function DLL_TEST7B:-
DLL_TEST7B:             ;will be exported as an ordinal
PUSH 40h                ;information+ok button only
PUSH 'Testbug - calling a function by ordinal'
PUSH OFFSET DLLMESS5
PUSH 0
CALL MessageBoxA        ;wait till ok pressed
RET

Calling a function directly by ordinal
GoAsm and GoLink provide a very simple way of calling an ordinal function in a Dll using the following code in the assembler source script (for example):-

CALL Testbug2:12
CALL Testbug2:13
The first call is to Testbug2.dll function number 12, which is yet another message box to demonstrate this working. Inside the Dll source code the function is preceded by EXPORT:12 to set up the export by ordinal.
The second call is to Testbug2.dll function number 13. Unlike the first call, only the ordinal number appears inside the Dll. This is achieved using the following source code in the Dll (preceding the function):-
EXPORT:13:NONAME
This is why, when you look at this in the debugger, no name for the function is displayed. You might want to do this to make it more difficult to see what parts of your code do what. Set the breakpoint to DLL_TEST8 and run Test 8.

Calling a function by name-load
This demonstration shows how at run-time a Dll can be loaded and an API in the Dll can be called using GetProcAddress. This is useful if there is a possibility that a particular Dll is not available on the machine which is running the application. If that is the case and you call the API directly, the application will not load at all and you get the message "Missing Dll" or similar. This happens because when an application is loaded, the system tries to load all its Dlls as well. If one cannot be found the application cannot load.

Here we want to use the API HtmlHelpA which resides in the Windows Dll hhctrl.ocx. In earlier machines without the new help version loaded this Dll will be absent.

PUSH 'hhctrl.ocx'
CALL LoadLibraryA       ;load htmlhelp dll
OR EAX,EAX              ;see if failed
JZ >.exit               ;yes
PUSH 'HtmlHelpA'
PUSH EAX
CALL GetProcAddress     ;get address of html help
OR EAX,EAX              ;see if failed
JZ >.exit               ;yes
PUSH 0,0
PUSH "Testbug.chm"      ;compressed help file to show
PUSH [hWnd]	   ;window handle
CALL EAX	              ;call HtmlHelpA API
When finished with hhctrl.ocx you should call FreeLibrary.

If you want, at start-up you can get all the addresses of the APIs you want to call. I found code similar to this in the particularly nasty MTX virus, so I am pleased to put something so destructive to some valuable use. The code here is particularly compact and also acts as a good demonstrator of GoBug. What happens here is that the names of each API to be called is passed to GetProcAddress with the appropriate handle to the Dll containing the API (in this case all are in Kernel32.dll). The address is then stored ready to be called directly. One unusual feature about this code is that in its original version all the name strings and addresses were kept in the code section rather than in data. This made it much easier for the virus to infect executables because it could limit itself to one section in the executable. However, from Windows 98 the systems gives more protection to code areas and it does not allow a write to code at run-time. The API VirtualProtect can be called to remove this protection, but that API itself provides an output and it will fail if it cannot write to the output address. So this protection effectively stops this code from working. For this reason in this example the data section is used to keep the name strings and to store the addresses of the calls.
Set a breakpoint at DLL_TEST9 and run the test.
When the breakpoint is reached, in GoBug open an inspector to look at KERNEL_NAME (use the menu item "inspect, exe/dll data symbols", then "view as data") and enlarge the inspector until you see an area of memory labelled "CALLS".
Then single-step down the code and see the addresses being loaded into CALLS (you can use a code breakpoint and F9 to make this quicker).
A little further down in the code pane you will see two calls, the first to [CALLS+18h] and the second to GetTickCount. The first of these is a call to the address now inserted in memory at 24 bytes beyond the label CALLS. In fact, this is a call to GetTickCount, which was the last API address loaded by the loop containing GetProcAddress. The second is an ordinary call to GetTickCount to demonstrate that the first one works. Single-step (trace over) these calls using F6 and watch the eax register which will contain the output from these two calls. There will be a slight difference in the values in eax after these calls because the tick (the Windows clock tick) will have advanced a little between the two.
Here is the code used:-

DLL_TEST9:              ;calling function by name-load
PUSH ADDR KERNEL_NAME
CALL GetModuleHandleA   ;get handle of Kernel32.dll
MOV EBX,EAX             ;keep in ebx
MOV EDI,ADDR CALLS      ;get place to put API addresses
MOV ESI,ADDR NAME_STRINGS    ;get list of API names
L1:
PUSH ESI,EBX
CALL GetProcAddress
OR EAX,EAX              ;see if successful
JZ >L4                  ;no
STOSD                   ;insert API address into CALLS
L2:
LODSB                   ;get to end of this API name
OR AL,AL                ;see if end of string yet
JNZ L2                  ;no
LODSB
CMP AL,0FFh             ;see if finished function list
JZ >L3                  ;yes
DEC ESI
JMP L1
L3:
CALL [CALLS+18h]        ;ie. GetTickCount
CALL GetTickCount       ;proof correct thing was called
L4:
RET
;****** now for the strings (originally all held in code but since W98, in data)
DATA SECTION
KERNEL_NAME  DB 'KERNEL32.DLL',0
NAME_STRINGS DB 'CopyFileA',0
             DB 'DeleteFileA',0
             DB 'lstrlen',0
             DB 'lstrcat',0
             DB 'GetSystemDirectoryA',0
             DB 'GetWindowsDirectoryA',0
             DB 'GetTickCount',0
             DB 0FFh
;****** and where the addresses will be put
CALLS DD 7 DUP 0

Testing who called start-up code
As we saw when looking at start-up code and run-time link, when a Dll is loaded at run-time it is the application's own thread (ie the thread calling LoadLibrary) which is used to call the start-up code in the Dll. This test shows that the application's own main thread is also used to run the start-up code when a Dll is loaded in the usual way at start-up.
When watching this code with a debugger, use the breakpoint DLL_TEST10 in Testbug3.Dll. Then run Dll Test 10. You can see from the code fragment below that on start up the value of the Thread Environment Block (a thread-specific value obtained at FS:[18h]) is kept in memory. This same value is then put in the first part of the message box when Test 10 is run. The second part of the message box shows the address of the Thread Environment Block for the thread calling the function in the Dll. This of course is the application's main thread. The two values are the same proving that the same thread is used in each case.
First here is the start-up code in Testbug3.Dll which keeps the value at FS:[18h] in the data symbol KEEPTEB:-

MAIN:                   ;start-up code
PUSH EBP                ;receiving 3 parameters from caller
MOV EBP,ESP
;now [EBP+8]=hInstDLL, [EBP+0Ch]=fdwReason, [EBP+10h]=reserved
CMP D[EBP+0Ch],1        ;see if DLL_PROCESS_ATTACH
JNZ >L30                ;yes
FS MOV EAX,[18h]        ;to see which thread calls this
MOV [KEEPTEB],EAX
L30:
MOV EAX,1               ;return true to continue loading/action
MOV ESP,EBP
POP EBP
RET 0Ch
Now when DLL_TEST10 is called, the value of KEEPTEB and also the TEB for the current thread are written into the message DLLMESS9 (M9 and M10 are labels at the appopriate place in that message).
DLL_TEST10:
MOV EDI,ADDR M9
MOV EAX,[KEEPTEB]
CALL HEXROTATE8         ;write hex number from eax into edi
MOV EDI,ADDR M10
FS MOV EAX,[18h]
CALL HEXROTATE8         ;write hex number from eax into edi
PUSH 40h                ;information+ok button only
PUSH 'Testbug - Testing who called the start-up code'
PUSH ADDR DLLMESS9
PUSH 0
CALL MessageBoxA        ;wait till ok pressed
RET
See also how to use the thread information block given by FS.


Iczelion's Win32asm tutorial 17: Dynamic Link Libraries

Tutorial 17: Les DLL
(Dynamic Link Libraries)
Bibliothèques de Liaison, Dynamiques

Dans ce Tutorial, nous allons voir les DLLs, ce qu'elles sont et comment les créer.
Vous pouvez télécharger l'exemple ici.

Théorie:

Si vous programmez depuis un petit moment déjà, vous constaterez que les programmes que vous avez l'habitude d'écrire ont quelques routines de code en communs. C'est vraiment une perte de temps de les récrire à chaque fois que vous commencez à développer de nouveaux programmes. Revenons en arrière, aux vieux jours du DOS, les programmeurs ont faits en sorte de stocker les routines les plus généralement employées dans une ou plusieurs bibliothèques. Quand ils veulent utiliser les fonctions, ils ont juste à lier la bibliothèque avec le fichier d'objet, et le linker extrait les fonctions de la bibliothèque puis les insère dans le fichier final exécutable. Ce processus est appelé la jonction statique. Les bibliothèques 'Runtime du C' sont de bons exemples. L'inconvénient de cette méthode c'est que vous avez des fonctions identiques dans chaque programme qui les appelle. Votre espace disque est gaspillé stockant plusieurs copies identiques des fonctions. Mais pour les programmes sous DOS, cette méthode est tout à fait valable puisqu'il n'y a généralement qu'un seul programme en action dans la mémoire. Ainsi il n'y a aucune perte précieuse de mémoire.
Sous Windows, la situation devient beaucoup plus critique parce que vous pouvez avoir plusieurs programmes tournant simultanément. La mémoire sera rapidement saturée si votre programme est vraiment grand. Windows a une solution pour ce type de problème : les bibliothèques de liaison dynamiques. Une bibliothèque de liaison dynamique est une sorte de regroupement commun de plusieurs fonctions. Windows ne chargera pas en mémoire plusieurs copies d'une dll, même dans le cas où il y a beaucoup de programmes tournant en même temps. Il fera seulement une copie en mémoire de la DLL que le programme emploie. Bon, je dois clarifier un peu ce point. En réalité, tous les process qui emploient la même dll, auront leurs propres copies de cette dll. C'est comme si il y avait plusieurs copies de cette dll en mémoire. Mais en réalité, Windows nous fait un petit tour de magique en paginant tous les morceaux de process utilisant la même dll. Ainsi en mémoire physique, il y a uniquement une copie du code de la dll. Cependant, chaque process aura sa propre section de données unique de la DLL.
Le programme se lie avec une DLL pendant l'exécution, à la différence de la vieille bibliothèque statique. C'est pourquoi on l'appelle 'bibliothèque de liaison dynamique'. Vous pouvez aussi vous défaire d'une DLL pendant l'exécution quand vous n'en avez plus besoin. Si ce programme est le seul à employer la DLL, il la virera immédiatement de la mémoire. Mais si la DLL est toujours employée par d'autres programmes, la DLL reste en mémoire tant que le dernier programme qui l'emploie s'en défasse.
Cependant, le linker a un travail plus difficile quand il exécute l'adresse fixups pour le fichier final exécutable. Puisqu'on ne peut pas "extraire" les fonctions et les insérer complètement dans le fichier final exécutable, d'une façon ou d'une autre on doit stocker assez d'informations personnelles sur la DLL et ses fonctions dans le fichier executable final pour être capable de charger correctement la DLL pendant l'exécution.
C'est là ! que la bibliothèque d'importation entre en jeu. Une bibliothèque d'importation contient des informations à propos de la DLL qu'il représente. Le linker peut extraire des bibliothèques d'importation les renseignements dont il a besoin. Quand le loader de Windows charge le programme en mémoire, il voit que le programme se lie avec une DLL donc il essai de trouver cette DLL et la réécrit alors dans l'espace d'adresse du process(du programme), puis exécute les adresses hors du programme pour les fonctions Call qui appellent la DLL.
Vous pouvez vouloir charger la DLL vous-même sans compter sur le loader (chargeur) de Windows. Cette méthode a son pour et son contre :
  • S'il n'y a pas besoin d'une bibliothèque d'importation alors vous pouvez charger et employer n'importe qu'elle DLL même si elle n'est accompagnée d'aucune bibliothèque d'importation. Cependant, vous avez quand même besoin de savoir certaines choses sur les fonctions qu'elle vous propose, des choses comme combien de paramètres elles utilisent…
  • Si vous laissez le loader charger la DLL pour votre programme, et que le loader ne trouve pas cette DLL il annoncera "Le fichier .DLL exigé, xxxxx.DLL est manquant" et paf! Votre programme n'a aucune chance d'être lancé même si cette DLL n'est pas essentiel pour son fonctionnement minimal. Si vous chargez la DLL vous-même, et que la DLL ne peut pas être trouvé et que ce n'est pas essentiel pour l'opération, votre programme le fait remarquer à l'utilisateur mais peut continuer.
  • Vous pouvez utiliser et appeler d'autres fonctions *non décrites dans ces Tuts* qui ne sont pas incluses dans les bibliothèques d'importation. À condition que vous connaissiez assez de renseignements sur ces fonctions.
  • Si vous utilisez LoadLibrary, vous devez appeler GetProcAddress pour chaque fonction que vous souhaitez appeler. GetProcAddress retrouve l'adresse de l'entrypoint d'une fonction à l'intérieur d'une DLL particulière. (l'adresse où commence une fonction particulière). Donc il se peut que votre code soit un peu plus grand et plus lent, mais ça change pas grand chose.
En comparant les avantages / inconvénients d'un appel à LoadLibrary, nous entrons maintenant dans les détails pour créer une DLL.
Le code suivant est le squelette de la DLL. (DLL skeleton.)

;--------------------------------------------------------------------------------------
; DLLSkeleton.asm
;--------------------------------------------------------------------------------------
.386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib

.data
.code
DllEntry proc hInstDLL:HINSTANCE, reason:DWORD, reserved1:DWORD
mov eax,TRUE
ret
DllEntry Endp
;---------------------------------------------------------------------------------------------------
; C'est une fonction factice
; Ça ne fait rien. Je l'ai mise ici pour montrer où vous pouvez insérer des fonctions à l'intérieur d'une DLL.
;----------------------------------------------------------------------------------------------------
TestFunction proc
ret
TestFunction endp

End DllEntry

;-------------------------------------------------------------------------------------
; DLLSkeleton.def
;-------------------------------------------------------------------------------------
LIBRARY DLLSkeleton
EXPORTS TestFunction

Ce tout petit programme ci-dessus est le squelette de la DLL. Chaque DLL doit avoir une fonction d'entrypoint. Windows appellera la fonction d'entrypoint chaque fois que :

  • La DLL est loadée en premier
  • La DLL est unloadée (libérée)
  • Un lien est créé dans ce même process
  • Un lien est détruit dans ce même process
DllEntry proc hInstDLL:HINSTANCE, reason:DWORD, reserved1:DWORD
mov eax,TRUE
ret
DllEntry Endp

Vous pouvez nommer la fonction DllEntry comme vous le souhaitez tant que vous avez une FIN correspondante . Cette fonction prend trois paramètres, dont seulement les deux premier sont importants.
hInstDLL est l'handle du programme qui a appeler cette DLL. Ce n'est pas le même que l'instance handle du process. Vous devez garder cette valeur si vous avez besoin de l'employer plus tard. Vous ne pourrez plus l'obtenir de nouveau aussi facilement. Exemple: Si vous connaissez la DLL Kernel32.DLL vous savez bien qu'elle possède plusieurs fonctions ou modules à l'intérieurs d'elle, dont GetCommandeLineA et ExitProcess.
reason peut être une des quatre valeurs :

  • DLL_PROCESS_ATTACH La DLL reçoit cette valeur quand elle est injectée la toute première fois dans l'espace d'adresses du process. Vous pouvez utiliser cette occasion pour faire l'initialisation.
  • DLL_PROCESS_DETACH La DLL reçoit cette valeur quand elle est unloadée (supprimée) de l'espace d'adresses du process. Vous pouvez employer cette occasion pour faire un peu de nettoyage comme désinffecter la mémoire etc…
  • DLL_THREAD_ATTACH La DLL reçoit cette valeur quand le process crée un nouveau lien. (la DLL était déjà utilisée par un autre programme, elle est déjà en mémoire)
  • DLL_THREAD_DETACH La DLL reçoit cette valeur quand un lien dans le process est détruit.
Renvoyez TRUE dans eax si vous souhaitez que la DLL continue son exécution. Si vous renvoyez FALSE, la DLL ne sera pas chargée. Par exemple, si votre code d'initialisation doit réserver de la mémoire et qu'il ne peut pas faire ça avec succès, la fonction DllEntry doit renvoyer FALSE pour indiquer que la DLL ne peut pas être lancée.
Vous pouvez placer vos fonctions dans la DLL après la fonction DllEntry ou même avant elle. Mais si vous voulez qu'elles soient CALLables par d'autres programmes, vous devez mettre leurs noms dans un fichier d'exportation (.def).
Une DLL a besoin d'un fichier de définition de module dans son process liée à son développement. Nous y jetons un coup d'œil maintenant.

LIBRARY DLLSkeleton
EXPORTS TestFunction

Normalement dans la première ligne vous devez avoir la déclaration LIBRARY qui sert à définir le nom du module interne (la fonction souhaitée) de la DLL. Vous devez correspondre avec ce module grâce au nom du fichier de la DLL.
La déclaration EXPORTS dit au linker quelles fonctions sont en train de tourner dans la DLL, c'est-à-dire quelles fonctions sont déjà utilisées par d'autres programmes. Dans l'exemple, nous voulons que d'autres modules soient capables d'appeler TestFunction, donc nous mettons son nom dans la déclaration EXPORTS.
Un autre type de changement est possible grâce au commutateur du linker. Vous devez mettre le commutateut /DLL et /DEF:<votre nom de fichier def> dans votre linker de commutation comme cela :

link /DLL /SUBSYSTEM:WINDOWS /DEF:DLLSkeleton.def /LIBPATH:c:\masm32\lib DLLSkeleton.obj

Les commutateurs d'assembleur sont les mêmes, à savoir /c /coff /Cp. Ainsi après que vous ayez lié le fichier objet, vous obtiendrez *.DLL et *.lib. Le *.lib est la bibliothèque d'importation que vous pouvez employer pour vous lier avec d'autres programmes qui emploient les fonctions de votre DLL.
Ensuite je vous montrerai comment employer LoadLibrary pour charger une DLL.

;---------------------------------------------------------------------------------------------
; UseDLL.asm
;----------------------------------------------------------------------------------------------
.386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\user32.lib

.data
LibName db "DLLSkeleton.dll",0
FunctionName db "TestHello",0
DllNotFound db "Cannot load library",0
AppName db "Load Library",0
FunctionNotFound db "TestHello function not found",0

.data?
hLib dd ? ; l' handle de la librairie (l' handle de la DLL)
TestHelloAddr dd ? ; l'adresse de la function TestHello

.code
start:
invoke LoadLibrary,addr LibName
;---------------------------------------------------------------------------------------------------------
; Appelle LoadLibrary avec le nom de la DLL désirée. Si l'appel est couronné de succès
; Il renverra l'handle de la bibliothèque (DLL). Sinon, il renverra le NULL.
; Vous pouvez passer l'handle de la bibliothèque à GetProcAddress ou n'importe quelle fonction qui a besoin
; de l'handle d'une DLL comme paramètre.
;------------------------------------------------------------------------------------------------------------
.if eax==NULL
invoke MessageBox,NULL,addr DllNotFound,addr AppName,MB_OK
.else
mov hLib,eax
invoke GetProcAddress,hLib,addr FunctionName
;-------------------------------------------------------------------------------------------------------------
; Quand vous obtenez l'handle de la bibliothèque, vous le passez à GetProcAddress avec l'adresse
; du nom de la fonction de la DLL que vous souhaitez appeler. Ceci renvoie l'adresse
; de la fonction si tout s'est passé avec succès. Autrement, il renvoie un NULL.
; Les adresses des fonctions ne changent pas à moins que vous ne déchargiez puis rechargiez la bibliothèque.
; Donc vous pouvez les placer dans des variables globales pour de futures utilisations.
;-------------------------------------------------------------------------------------------------------------
.if eax==NULL
invoke MessageBox,NULL,addr FunctionNotFound,addr AppName,MB_OK
.else
mov TestHelloAddr,eax
call [TestHelloAddr]
;-------------------------------------------------------------------------------------------------------------
; Ensuite, vous pouvez appeler la fonction avec un appel simple avec la variable contenant
; l'adresse de la fonction comme opérande.
;-------------------------------------------------------------------------------------------------------------
.endif
invoke FreeLibrary,hLib
;-------------------------------------------------------------------------------------------------------------
; Quand vous n'avez plus besoin de la bibliothèque, vous la déchargez avec FreeLibrary.
;-------------------------------------------------------------------------------------------------------------
.endif
invoke ExitProcess,NULL
end start

Donc vous pouvez voir que l'utilisation de LoadLibrary est un peu plus compliquée mais elle est aussi plus flexible.

[Iczelion's Win32 Assembly Homepage]

Traduit par Morgatte


ASSEMBLER UNE DLL

Si vous avez parcouru les Tuts d'Aczelion, vous avez dû voir dans le Tut 17 comment programmer vos propres DLL (Dynamic Link Libraries), il donne également une brève explication pour les assembler mais revenons sur ce point. Je ne vais pas m'attarder de trop, on va passer à la pratique, ça vaut bien toutes les explications.

Faites l'exemple avec moi:

Je pratique sur la DLL du Tut24 'MouseHook.asm'. D'abord il faut étudier comment elle a été construite et c'est extrêmement important. Car pour compiler une DLL il faut deux fichiers. le fichier *.asm et le fichier *.def qui en découle.

;--------------
;MouseHook.asm
;--------------
.386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib
include \masm32\include\user32.inc
includelib \masm32\lib\user32.lib

.const
WM_MOUSEHOOK equ WM_USER+6
.data
hInstance dd 0

.data?
hHook dd ?
hWnd dd ?

.code
DllEntry proc hInst:HINSTANCE, reason:DWORD, reserved1:DWORD ;<-- on commence toujours pas
    push hInst				   		; déclarer le point d'entrée
    pop hInstance					  	; de la DLL.
      mov  eax,TRUE						; Mais peu importe...
      ret
DllEntry Endp

MouseProc proc nCode:DWORD,wParam:DWORD,lParam:DWORD	; Ce qui nous intéressent ce sont les
    invoke CallNextHookEx,hHook,nCode,wParam,lParam	; noms des fonctions appartenant à la
    mov edx,lParam					; DLL
    assume edx:PTR MOUSEHOOKSTRUCT
    invoke WindowFromPoint,[edx].pt.x,[edx].pt.y
    invoke PostMessage,hWnd,WM_MOUSEHOOK,eax,0
    assume edx:nothing
    xor eax,eax
    ret
MouseProc endp

InstallHook proc hwnd:DWORD
    push hwnd
    pop hWnd
    invoke SetWindowsHookEx,WH_MOUSE,addr MouseProc,hInstance,NULL
    mov hHook,eax
    ret
InstallHook endp

UninstallHook proc
    invoke UnhookWindowsHookEx,hHook
    ret
UninstallHook endp

End DllEntry

Et voilà à partir du contenu de la DLL on va pouvoir créer le fichier *.def. Puisqu'on sait que notre MouseHook.asm contient les fonctions MouseProc, InstallHook et UninstallHook, le fichier *.def sera ainsi définit:
;-------------
;MouseHook.def
;-------------
LIBRARY MouseHook  ; <-- c'est le nom de votre DLL

EXPORTS		   ; <-- et les fonctions qu'elle met à notre disposition.
  MouseProc
  InstallHook
  UninstallHook
Voilà vous écrivez ça dans MouseHook.def

Maintenant nous avons les deux fichiers en question (*.asm et *.def), nous pouvons passer à la phase d'assemblage de la DLL en elle-même.

D'abord sachez que j'ai créé un dossier spécial assemblage en tout genre dans mon dossier Masm32. Mon dossier s'appelle C:\Masm32\_essai et dedans se trouvent les petits outils indispenssables que sont :
- ML.exe
; - RC.exe (+Rcdll.dll)
; ça c'est pour passer - LINK.exe (+Mspdb50.dll)
; d'un *.asm --> *.exe - Cvtres.exe
; Normalement ils sont présent dans c:\Masm32\bin
Pour une DLL vous devez en plus rajouter:
- Lib.exe

Allons-y gaiement:
J'ouvre une fenêtre DOS et je me déplace dans ce répertoire:
cd\
cd Masm32
cd _essai
Pour vous se sera normalement cd bin

ET VOICI LA VÉRITABLE OPÉRATION:

(Les majuscules et minuscules n'ont pas d'importance sur le nom de votre DLL mais elles en ont pour le reste. Attention au paramètre /Cp par exemple.)
ml /c /coff /Cp mousehook.asm
link /DLL /NOENTRY /def:mousehook.def /subsystem:windows mousehook.obj

J'ai automatisé ce processus dans un fichier *.bat nommé Assembles_DLL.bat. Vous pourrez vous en servir en plaçant Assembles_DLL dans ..C:\Masm32\Bin ou pour moi ..C:\Masm32\_essai. Placez dans le même répertoire vos deux fichiers *.asm et *.def sous le nom de 'DLL.asm' et 'DLL.def'. Exécutez 'Assembles_DLL' et vous obtiendrez une DLL du nom de (Et non pas DLL.dll) Nom_qui_est_dans_DLLdef.dll. Il sera donc inutile de la renommer.

Par Morgatte


Accueil

Accueil CHM

Normal, dit comme ça,
vous ne risquez pas de comprendre.


Building DLLs in MASM32

How to build a DLL in MASM32

Short History

A long time ago in the days of early 16 bit Windows the available computer hardware required to run it was a 286 with 1 meg of memory. This was a very ambitious task for such a humble device yet the code design was so efficient that it could run a graphics user interface in as little as 1 meg of memory.

A 286 only allowed what was called "task switching" which was effectively only running one program at a time but from the 386 onwards, the software simulated multitasking so that more than 1 program could run at a time. To get such an interface to run on such limited hardware, the operating system had to conserve memory and also find alternatives so that the machine could run.

Virtual memory was disk space that was counted as memory so that non running programs could be swapped out to disk while others continued to run in the actual memory available but another method was also used that made it possible to run programs that were far bigger than the available memory in the computer, this was the Dynamic Link Library.

In comparison a DOS program was a monolith, it was usually loaded complete into memory and was therefore limited to the available memory that was available in DOS which was 640k minus all of the loaded device drivers that DOS used. There were a few attempts to solve this problem in DOS by using what was called "overlays" and a language like Quick Basic used a runtime EXE file if the EXE was compiled with that option but generally they did not have the power or flexibility that early 16 bit Windows had.

A Dynamic Link Library was very similar to an executable file with one exception, it did not have a starting point like a normal executable file because it was designed to perform a different function. An executable file must have a start so that it can be loaded by the operating system and run in the normal manner.

A DLL is executable code that is loaded into memory on demand and different parts of the executable code can be called and run by the calling program. A DLL can also be called by more than one program at a time which is primarily how the large operating system DLLs work. This made it possible for many programs to use the same code from the operating system insteads of having to duplicate it every time a program ran. It also allowed programmers to standardise the interface of programs so that users did not have to learn a new interface every time they started a new program.

In modern versions of Windows a DLL can still be loaded in a couple of ways, it can be loaded when the program starts and remain in memory as long as the program that loads it runs or it can be loaded on demand, used then unloaded when it is finished.

How do you write a DLL ?

The form of a DLL is determined by the operating system and there are a number of considerations when laying out the basic DLL code.

The code entry point must conform to a particular specification which is 3 DWORD parameters passed in the normal manner on the stack and for the DLL to continue loading once this address has received the parameters, it must return TRUE in the EAX register.

At the bare minimum you would write a starting procedure like follows,

LibMain proc parameter1:DWORD, parameter2:DWORD, parameter3:DWORD

    mov eax, TRUE
    ret

LibMain endp

If the starting procedure is not in the form shown, you will usually get the following warning/error from the linker.

warning LNK4086:
entrypoint "_XXXXX" is not __stdcall with 12 bytes of arguments; image may not run

The _XXXXX is the name of the first procedure or first label in the code.

The LibMain is in fact a lot more useful than this, it can be used to initialise data at startup and it can be used to do cleanups at shutdown because of the information that is passed to it at startup. In the DLL_PROCESS_ATTACH constant in the following .IF block, you can set global values like the instance handle, start a memory mapped file if you need to share data in that manner, allocate memory for later tasks while the DLL is running or anything that you need to have set up at startup.

A fuller LibMain looks like the following,

; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««

LibMain proc hInstDLL:DWORD, reason:DWORD, unused:DWORD

    .if reason == DLL_PROCESS_ATTACH
      mov eax, TRUE

    .elseif reason == DLL_PROCESS_DETACH
      ; ---------------------------------------
      ; perform any exit code you require here
      ; ---------------------------------------

    .elseif reason == DLL_THREAD_ATTACH

    .elseif reason == DLL_THREAD_DETACH

    .endif

    ret

LibMain endp

; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««

When the DLL is shut down you can process the DLL_PROCESS_DETACH member of the .IF block to deallocate memory or flush files to disk or any other task where you need to clean up after the program has finished.

The first parameter "hInstDLL" passed to the LibMain is the Instance handle of the DLL which is the actual starting address of the DLL in memory and this is useful among other things if the DLL is going to have resources as the DLL instance handle is necessary to access the resources.

The second parameter "reason" determines how the DLL is being called and whether it is the startup call or the shutdown call. This is how you can organise to initialise data at startup and do the cleanup at shutdown.

After the LibMain is where you write the procedures that you need to have in the DLL but importantly, after all of the procedures you must terminate all of the code with a directive si that MASM know where the end of the source file is.

Terminating the code

While a normal EXE file has a label to tell it where to start, A DLL must be written with a procedure at the start so if the procedure name is,

LibMain

You must terminate the code with,

end LibMain

Exporting procedures from the DLL

Procedures are no different to normal code in MASM but the programmer has the choice of what can be accessed in the DLL and what is an internal procedure. This is more or less the normal organisation of any program and the programmer has control of what is visible and what is not.

This is done with the DEFINITION file that is used by the linker to determine what procedure are exported. The syntax for a definition file is very simple, you must tell the linker what the internal name of the DLL will be then you must tell the linker what the name of each exported procedure will be.

This is the content of the definition file for the example that accompanies this tutorial.

LIBRARY dlltute
EXPORTS MessageBoxSTD
EXPORTS MessageBoxCC
EXPORTS MessageBoxFC

When the library is built and subsequently read and displayed by a program like DumpPE, you get this information,

Exp Addr Hint   Ord Export Name by dlltute.dll - Thu Jun  5 00:00:34 2003
 -------- ---- ----- ---------------------------------------------------------
 00001080    0     1 MessageBoxCC
 000010D8    1     2 MessageBoxFC
 00001026    2     3 MessageBoxSTD

This is the interface within the DLL that allows the calling program to call the exported procedures within the DLL.

How do you build a DLL ?

Assembling the DLL code is identical to a normal EXE file and it is done as normal with the following line.

\masm32\bin\ml /c /coff dlltute.asm

The option "/c" means only assemble the file into an object module. The "/coff" for version 6.14 of ML.EXE means build the object module as a "coff" type module, not the older OMF format.

Linking a DLL is different to building an EXE file.

\masm32\bin\Link /SUBSYSTEM:WINDOWS /DLL /DEF:dlltute.def dlltute.obj

The linker options are as follows, the option "/DLL" tells the linker to place the correct startup code for a DLL at the beginning of the DLL, the line "/DEF:dlltute.def" tells the linker where to find the list of exported procedures and the internal name of the Dynamic Link Library.

If you are using resources in the DLL, you can either add the compiled resources in the RES file as the last item on the link line or you can convert the RES file directly to an object file with the utility CVTRES.EXE and add the resource object file to the end of the link line.

Using the DLL from the calling program

With the two methods that can be used, the choice is based on what is the most efficient for the application. If the code is to be used for short duration it is worth dynamically loading it by using the 3 API functions LoadLibrary(), GetProcAddress() and FreeLibrary(). This allows you to keep your memory usage down and while this may not matter with small applications, if you are writing very large DLLs, the combined memory usage of more than one DLL at a time can restrict the final functionality of the program if memory become low because of the number of DLLs loaded.

If the application needs the functionality of the DLL for its full running time, the DLL should be loaded when the program starts. The linker when it builds a DLL also builds an EXPORT library that is designed to be linked into both MASM and Microsoft C/C++ programs so that the DLL can be used at startup. What you would normally do is write a prototype for each exported procedure in your DLL and put them in an include file so that when you want to use the DLL in your program, all you need to do is include the prototypes and the library and have the DLL where your program can find it.

---------- o ----------


ASM : Créer une dll - CodeS SourceS

Ce document intitulé « Créer une dll » issu de CommentCaMarche (codes-sources.commentcamarche.net)

Description

Cet example montre comment faire une dll en assembleur sans utiliser d'api. Avec très peut de code.

.486
.model		flat,stdcall
.code

DLLMain	proc	p1,p2,p3
        mov	eax,1

        ret
DLLMain	endp

;additionne p2 et p3 resultat dans p1

Addptr2	proc	export p1:ptr,p2,p3
        push	  esi
        mov	  esi,p1
        mov	  eax,p2
        add	  eax,p3
                mov 	  [esi],eax
        mov	  eax,0
        pop	  esi

        ret
Addptr2	endp

;additionne p1 et p2 la fonction retourne le resultat

Add2		proc	export p1,p2

        mov	  eax,p1
        add	  eax,p2

            ret
Add2		endp

;multiplie p1 par p2 la fonction retourne le resultat

Mull2       proc export p1:WORD,p2:WORD

               movsx      eax,p1
               movsx      ebx,p2
               imul         ebx

              ret
Mull2       endp

;met les valeurs p2,p3 et p4 dans la stucture p1

CrStruc   proc export p1:ptr,p2,p3,p4

            push    esi
   xxx         mov     esi,p1
            mov     eax,p2
            mov     [esi],eax
            mov     eax,p3
            mov     [esi+4],eax
            mov     eax,p4
            mov     [esi+8],eax
            pop      esi

            ret
CrStruc   endp

end		DLLMain

Le fichier Makeit.BAT

@echo off
c:\masm32\bin\ml /c /coff dll.asm
c:\masm32\bin\link /dll /DEF:dll.def dll.obj
:: del dll.obj
:: del dll.exp
:: del dll.lib
pause

Le fichier dll.DEF

LIBRARY dll
EXPORTS Addptr2
EXPORTS Add2
EXPORTS Mull2
EXPORTS CrStruc


MASM librairies

Autres Sujets

Mettre a jour les librairies masm32
Utiliser les DLL
Pourquoi utiliser Statiquement ou dynamiquement les dll ?

Créer une librairie a partir de ses définitions de prototypes
 Utiliser le C++ et MASM

MASM32 fournit les moyens de se constituer une librairie personnelle.Cela permet d'intégrer facilement dans n'importe quel programme ses propres routines les plus souvent utilisées.En cas de debuggage de ces mêmes routines,il suffit alors de recompiler les programmes qui les utilisent.Inconvénient: les fonctions doivent être documentées avec la même rigueur que winhelp , prendre winhelp comme modèle.

La marche a suivre est la suivante:

créer un répertoire (c:\masm32\persolib)

placer make.bat dans ce répertoire

;----------------------------------- début fichier make.bat----------------------------------------------------------

;----------------------------------- fin de fichier make.bat ----------------------------------------------------------

L'instruction "pause" n'est pas optionnel.Windows fonctionnant en multitache,multithread , commence par éxécuter le link puis passe à la suppression des fichiers objets.La suppression est beaucoup plus rapide que link,le résultat est que la librairie produite est vide.L'éxécution asynchrone des fichiers batchs ressemble a un bug windows.

Former le Code du fichier Objet

Insérer un PROC dans le fichier suivant

    ;------------------------------------------------Squelette de création de l'objet----------------------

    ; #########################################################################

    .386 ; force 32 bit code

    .model flat, stdcall ; memory model & calling convention

    option casemap :none ; case sensitive

    ; --------------------------------

    ; Les fichiers inclus içi ,suprimer les points virgules:NE déclarer AUCUNE LIBRAIRIE

    ;c'est au programme utilisateur de rajouter les .lib nécessaires.

    ; --------------------------------

    include \masm32\include\windows.inc

    include \masm32\include\lz32.inc

    include \masm32\include\user32.inc

    include \masm32\include\kernel32.inc

    ; include \masm32\include\masm32.inc

    ; include macros.txt

    ; ---------------------------------------------------------

    ; Les PROTO sont tous regroupés dans un fichier .inc du même

    ; nom que la librairie .lib

    ; ---------------------------------------------------------

    ;lorsque un proc en utilise un autre,syntaxe externdef nom : définition

    ; EXTERNDEF LongZtexte:PROTO :DWORD,:DWORD

    ; EXTERNDEF EtatMemoirePile :MEMORY_BASIC_INFORMATION

    ; EXTERNDEF PoriginePile:DWORD

    .data ;les données placées içi ne seront reconnus que par cette routine

    .code

    ;################################################################

    ;insérer le PROC içi

    ;################################################################

    end ;et non pas end start,signe le fichier librairie

    ;---------------------------- fin de fichier ----------------------------------------------

assembler chaque proc séparément pour vérifier son fonctionnement
Procéder de même pour chaque PROC

Lancer MAKE, fini !

          Documenter la fonction en utilisant le HTML Help Wokshop

Mise à jour des librairies de MASM32

Trois utilitaires permettent de gérer les problèmes liés aux librairies

inc2l,malgré quelques défauts,extrait la librairie d'une dll lorsqu'on lui fournit un fichier inclus comportant les déclarations de PROTO des fonctions fournis par la DLL.Voir MSDN pour écrire les nouveaux protos,on peut se servir de l'ancien .inc pour éviter de tout réécrire.Les nouvelles définitions de protos doivent être exacts et comporter la taille des arguments passés a l'appel de la fonction.On ne peut pas se simplifier le travail en ne mettant que le nom de la fonction.Mettre l'inclus dans un répertoire et appeler inc2l en lui passant le nom de l'inclus

l2inca: Extrait les protos d'une librairie,mieux vaut utiliser protolib (editmasm)

l2incu:même chose que l2inca mais pour la convention UNICODE ,fonctions se terminant par W

LIB:création de la librairie a partir des fichiers objets

Utilisation statique et dynamique des DLL (adresses des API fixes ou recherchées)

De manière statique:

    On rajoute les déclarations de proto et la librairie aux sources

    include \MASM32\include\dbghelp.inc

    includelib \masm32\lib\dbghelp.lib

    Utilisation des fonctions

invoke SymSetOptions,options

De manière dynamique: (voir la page outils)

    Aucun fichier a inclure au source

    Déclarations:

      zSymSetOptions TYPEDEF PROTO :DWORD

      fSymSetOptions TYPEDEF PTR zSymSetOptions

      SymSetOptions TEXTEQU <fSymSetOptions ptr HSymSetOptions>

    data

      HSymSetOptions dd 0

    Charger la dll

      invoke LoadLibrary,SADD("dbghelp.dll")

    Obtenir l'adresse de la fonction

    invoke GetProcAddress,hLib,SADD("SymSetOptions")

    mov HSymSetOptions,eax

    Utilisation des fonctions

    invoke SymSetOptions,options

Mises A jour des librairies Windows:

Utilisation Statique:(adresse des APIS fixes)

Lorsque microsoft modifie les dll,les librairies sont elles aussi modifiées.Les adresses des fonctions APIS changent et ..... les programmes utilisant les vieilles librairies ne marchent plus.Il est nécessaire alors de recompiler avec les nouveaux inclus et les nouvelles librairies.

Utilisation Dynamique:

Plus de problèmes.L'adresse étant recherché dans la dll,celle ci peut avoir changée , cela n'a pas d'importance.

Microsoft fait attention au problème et recommande l'utilisation dynamique lorsque les APIS sont en cours de dévellopement et sont susceptibles de changer

Exemples:

dbghelp.dll , la dll des debugging tools change tous les six mois,utiliser la de manière dynamique

DirectX est en constante évolution , a utiliser dynamiquement

Les fonctions serveurs 2003 sont elles aussi susceptibles d'être modifiés,a utiliser dynamiquement de préfèrence

Kernel32.dll est a la base de tous les programmes,on peut utiliser le mode statique.

Exemple d'utilisation dynamique

Un exemple un peu poussé puisqu'il n'utilise que le chargement dynamique.C'est editmasm qui a créé les nombreuses déclarations nécessaires à l'aide des déclarations de protos contenus dans masm32

Bricolage:

Comment créer une librairie a partir d'une dll

Recréer les librairies

deux méthodes

Avec DUMPBIN et LIB /DEF:

1) dumpbin /EXPORTS nom.dll > nom.txt ,extrait le nom des fonctions exportés par la dll

2) créer le fichier def (celui servant a définir les fonctions exportés

lorsque l'on crée une dll)

LIBRARY nom

EXPORTS

;suite des noms repris de nom.txt

3) créer la librairie

lib /DEF:nom.def

La librairie créer ne peut être appeler que par un call

Créer le code d'une librairie sans code,ne contenant que l'appel

    ;----------------------------------------------------------

    .386

    .model flat,stdcall

    .code

    fonction1 proc param1:DWORD, param2:DWORD ...

    fonction1 endp

    ........2 et ainsi de suite

    end

    ;----------------------------------------------------------

    c'est le code d'une librairie,pas de "start: end start"

    créer le point def comme précédemment

    compiler

    ml /c /coff /Cp nom.asm

    link /DLL /NOENTRY /def:nom.def /subsystem:windows nom.obj

    on obtient une lib invokable, une dll vide de code,a effacer

Utiliser polib

C:\masm32\tools\INC2L\INC2L.EXE efface la dll

Le principe de fonctionnement est expliqué au dessus

Une librairie kernel a jour est nécessaire pour utiliser les exceptions avec editmasm
Préférez les librairies fournies par microsoft,quelques problèmes existent en les recréant.

On peut avoir des librairies a jour avec le SDK

Utiliser le C++ et MASM ,résoudre les problèmes d'incompatibilité avec d'autres systèmes

On peut utiliser des librairies C++ dans des programmes MASM.

Libcmt.lib vous oblige à modifier la structure habituelle de MASM et a utiliser un mot réservé

.code

start:

---- içi code--

end start

devient

.code

WinMain proc STDCALL public uses esi edi ebx,hInst:DWORD,hPrev:DWORD,\
lpCmdLine:DWORD,nShowCmd:SDWORD

WinMain endp
end

WinMain est le mot réservé pour désigner le point d'entrée du code (au lieu de start:,changeable).Le fichier crt0.c donne les mots réservés à utiliser en fonction du programme.Celui ci se trouve en C:\Program Files\Microsoft SDK\src\crt.

msvcrt.lib vous oblige à utiliser un manifest.Un fichier manifest est un fichier texte,modifiable avec notepad,dont le format d'écriture est le XML.

Manifest:

Un fichier manifest est identifié comme tel par le système,lorsqu'il possède l'extension .manifest.Il sert a aiguiller le système dans l'utilisation de tel ou tel librairie.Il joue un rôle particulier lorsqu'on utilise CRT,MFC et ATL.Il indique au système ou trouver les librairies Mt.exe est le tool permettant de les manipuler (SDK et C++).Le cas général,sans problème particulier,est résolu par celui-ci:

Il doit être présent dans le fichier ressource pour être opérationnel

#define RT_MANIFEST 24

#define APP_MANIFEST 1

APP_MANIFEST RT_MANIFEST DISCARDABLE "libs.manifest" //"Microsoft.VC80.CRT.manifest"

Au cas ou un programme doit utiliser une ancienne version de dll,le fichier manifest sert à déclarer la dépendance du programme vis à vis de ces DLL.Exemple le Microsoft.VC80.CRT.manifest.les librairies dépendantes doivent se trouver dans le même répertoire que le programme

Un fichier manifest peut aussi servir à déclarer l'utilisation des XP THEMES,common controls

Si vous avez deux .manifest à incorporer (un seul est possible) utiliser mt.exe pour les réunir.


Building DLLs in MASM32

How to build a DLL in MASM32

Get the example code

Short History

A long time ago in the days of early 16 bit Windows the available computer hardware required to run it was a 286 with 1 meg of memory. This was a very ambitious task for such a humble device yet the code design was so efficient that it could run a graphics user interface in as little as 1 meg of memory.

A 286 only allowed what was called "task switching" which was effectively only running one program at a time but from the 386 onwards, the software simulated multitasking so that more than 1 program could run at a time. To get such an interface to run on such limited hardware, the operating system had to conserve memory and also find alternatives so that the machine could run.

Virtual memory was disk space that was counted as memory so that non running programs could be swapped out to disk while others continued to run in the actual memory available but another method was also used that made it possible to run programs that were far bigger than the available memory in the computer, this was the Dynamic Link Library.

In comparison a DOS program was a monolith, it was usually loaded complete into memory and was therefore limited to the available memory that was available in DOS which was 640k minus all of the loaded device drivers that DOS used. There were a few attempts to solve this problem in DOS by using what was called "overlays" and a language like Quick Basic used a runtime EXE file if the EXE was compiled with that option but generally they did not have the power or flexibility that early 16 bit Windows had.

A Dynamic Link Library was very similar to an executable file with one exception, it did not have a starting point like a normal executable file because it was designed to perform a different function. An executable file must have a start so that it can be loaded by the operating system and run in the normal manner.

A DLL is executable code that is loaded into memory on demand and different parts of the executable code can be called and run by the calling program. A DLL can also be called by more than one program at a time which is primarily how the large operating system DLLs work. This made it possible for many programs to use the same code from the operating system insteads of having to duplicate it every time a program ran. It also allowed programmers to standardise the interface of programs so that users did not have to learn a new interface every time they started a new program.

In modern versions of Windows a DLL can still be loaded in a couple of ways, it can be loaded when the program starts and remain in memory as long as the program that loads it runs or it can be loaded on demand, used then unloaded when it is finished.

How do you write a DLL ?

The form of a DLL is determined by the operating system and there are a number of considerations when laying out the basic DLL code.

The code entry point must conform to a particular specification which is 3 DWORD parameters passed in the normal manner on the stack and for the DLL to continue loading once this address has received the parameters, it must return TRUE in the EAX register.

At the bare minimum you would write a starting procedure like follows,

LibMain proc parameter1:DWORD, parameter2:DWORD, parameter3:DWORD

    mov eax, TRUE
    ret

LibMain endp

If the starting procedure is not in the form shown, you will usually get the following warning/error from the linker.

warning LNK4086:
entrypoint "_XXXXX" is not __stdcall with 12 bytes of arguments; image may not run

The _XXXXX is the name of the first procedure or first label in the code.

The LibMain is in fact a lot more useful than this, it can be used to initialise data at startup and it can be used to do cleanups at shutdown because of the information that is passed to it at startup. In the DLL_PROCESS_ATTACH constant in the following .IF block, you can set global values like the instance handle, start a memory mapped file if you need to share data in that manner, allocate memory for later tasks while the DLL is running or anything that you need to have set up at startup.

A fuller LibMain looks like the following,

; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««

LibMain proc hInstDLL:DWORD, reason:DWORD, unused:DWORD

    .if reason == DLL_PROCESS_ATTACH
      mov eax, TRUE

    .elseif reason == DLL_PROCESS_DETACH
      ; ---------------------------------------
      ; perform any exit code you require here
      ; ---------------------------------------

    .elseif reason == DLL_THREAD_ATTACH

    .elseif reason == DLL_THREAD_DETACH

    .endif

    ret

LibMain endp

; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««

When the DLL is shut down you can process the DLL_PROCESS_DETACH member of the .IF block to deallocate memory or flush files to disk or any other task where you need to clean up after the program has finished.

The first parameter "hInstDLL" passed to the LibMain is the Instance handle of the DLL which is the actual starting address of the DLL in memory and this is useful among other things if the DLL is going to have resources as the DLL instance handle is necessary to access the resources.

The second parameter "reason" determines how the DLL is being called and whether it is the startup call or the shutdown call. This is how you can organise to initialise data at startup and do the cleanup at shutdown.

After the LibMain is where you write the procedures that you need to have in the DLL but importantly, after all of the procedures you must terminate all of the code with a directive si that MASM know where the end of the source file is.

Terminating the code

While a normal EXE file has a label to tell it where to start, A DLL must be written with a procedure at the start so if the procedure name is,

LibMain

You must terminate the code with,

end LibMain

Exporting procedures from the DLL

Procedures are no different to normal code in MASM but the programmer has the choice of what can be accessed in the DLL and what is an internal procedure. This is more or less the normal organisation of any program and the programmer has control of what is visible and what is not.

This is done with the DEFINITION file that is used by the linker to determine what procedure are exported. The syntax for a definition file is very simple, you must tell the linker what the internal name of the DLL will be then you must tell the linker what the name of each exported procedure will be.

This is the content of the definition file for the example that accompanies this tutorial.

LIBRARY dlltute
EXPORTS MessageBoxSTD
EXPORTS MessageBoxCC
EXPORTS MessageBoxFC

When the library is built and subsequently read and displayed by a program like DumpPE, you get this information,

Exp Addr Hint   Ord Export Name by dlltute.dll - Thu Jun  5 00:00:34 2003
 -------- ---- ----- ---------------------------------------------------------
 00001080    0     1 MessageBoxCC
 000010D8    1     2 MessageBoxFC
 00001026    2     3 MessageBoxSTD

This is the interface within the DLL that allows the calling program to call the exported procedures within the DLL.

How do you build a DLL ?

Assembling the DLL code is identical to a normal EXE file and it is done as normal with the following line.

\masm32\bin\ml /c /coff dlltute.asm

The option "/c" means only assemble the file into an object module. The "/coff" for version 6.14 of ML.EXE means build the object module as a "coff" type module, not the older OMF format.

Linking a DLL is different to building an EXE file.

\masm32\bin\Link /SUBSYSTEM:WINDOWS /DLL /DEF:dlltute.def dlltute.obj

The linker options are as follows, the option "/DLL" tells the linker to place the correct startup code for a DLL at the beginning of the DLL, the line "/DEF:dlltute.def" tells the linker where to find the list of exported procedures and the internal name of the Dynamic Link Library.

If you are using resources in the DLL, you can either add the compiled resources in the RES file as the last item on the link line or you can convert the RES file directly to an object file with the utility CVTRES.EXE and add the resource object file to the end of the link line.

Using the DLL from the calling program

With the two methods that can be used, the choice is based on what is the most efficient for the application. If the code is to be used for short duration it is worth dynamically loading it by using the 3 API functions LoadLibrary(), GetProcAddress() and FreeLibrary(). This allows you to keep your memory usage down and while this may not matter with small applications, if you are writing very large DLLs, the combined memory usage of more than one DLL at a time can restrict the final functionality of the program if memory become low because of the number of DLLs loaded.

If the application needs the functionality of the DLL for its full running time, the DLL should be loaded when the program starts. The linker when it builds a DLL also builds an EXPORT library that is designed to be linked into both MASM and Microsoft C/C++ programs so that the DLL can be used at startup. What you would normally do is write a prototype for each exported procedure in your DLL and put them in an include file so that when you want to use the DLL in your program, all you need to do is include the prototypes and the library and have the DLL where your program can find it.

---------- o ----------


ARTeam Tutorial

Code Squelettique d'une DLL

Nouveaux Concepts:

Bibliothèques et Jonctions
La procédure de la DLL EntryPoint
Le code squelettique pour créer des DLL
L'utilisation d'un fichier DEF pour exporter des fonctions

Les DLL ont quelques utilisations importantes dans le Reverse. Tout d'abord il est utile d'être capable d'écrire des plugins pour vos outils de reverse favoris comme ImpREC, Olly et PEID ou même votre IDE favori. Les Plugins prennent la forme d'une DLL utilisée par l'application.

L'utilisation la plus importante est l'injection d'une DLL dans laquelle une DLL peut être forcée à être chargée dans l'adresse espace d'un processus cible. Comme nous l'avons déjà vu avant, elle peut avoir accès à toute la mémoire du processus et contrôler l'application entière. Un bon exemple pour une utilisation, ce sont les articles sur l'unpacking avec des traceurs par NCR. Ceci est une structure avancée et dans ce Tutorial nous commencerons à apprendre et à comprendre comment fonctionnent les DLL et regarderons le code squelettique(Les bases du code) pour écrire cette DLL.

Les petits bouts de code ou de fonctions Fréquemment utilisés sont souvent stockés sous forme compilée à l'extérieur de l'exécutable qui les utilises dans des modules appelés des bibliothèques. Il y a 2 principaux types :

Bibliothèques Statiques

Ceux-ci sont utilisés principalement pendant le développement du programme. Dans un processus qui appelle la jonction statique, le linker(liaison) copie les fonctions exigées de la bibliothèque (qui d'habitude a l'extension .LIB) directement dans d'autres modules pour former le fichier exécutable final. Les bibliothèques, pendant l'exécution sont un bon exemple. L'inconvénient c'est qu'avec les exécutables, c'est l'utilisation des mêmes fonctions qui contiendra le code fait en double et consommera donc plus de RAM.

Bibliothèques de Liaison Dynamiques

Ceux-ci sont utilisés pendant l'exécution du programme. Dans ce cas, les fonctions de bibliothèque ne sont construites à aucun moment dans le fichier .EXE. Au lieu de cela ils résident dans un module séparé en formulaire exécutable (fichier au format PE) appelant une Bibliothèque de Liaison Dynamique. Cela contient des fonctions et des données qui peuvent être utilisées par un ou plusieurs autres modules ou "clients". Quand le premier client exige une DLL, Windows charge la DLL dans le client à l'adresse espace virtuel pour que le client puisse avoir accès aux fonctions dont il a besoin.

Les processus multiples qui chargent la même DLL à la même adresse de base partagent une seule copie de la DLL dans la mémoire physique. Le fait de faire cela, économise la mémoire du système et réduit l'échange. Tous les processus qui utilisent la même DLL partagent son code, mais ont leur copie unique propre de leur section de données.

Windows fournit deux méthodes pour dynamiquement lier une DLL; jonction implicite (une fois chargée) et jonction explicite (la durée d'exploitation). La différence se trouve avec le client; la DLL est la même dans les deux cas.

Jonction implicite(une fois chargée)

Windows charge la DLL automatiquement avec le premier programme du client et écrit les adresses de fonctions exigées dans la table d'adresse d'importation du client avant que le client ne commence l'exécution. C'est simple et n'exige aucun code supplémentaire, mais pour l'utiliser, l'application du client doit être lié avec une bibliothèque d'importation (le fichier .LIB) pour la DLL en utilisant la déclaration includelib.

Quand le module du client est construit, le linker(liaison ou lien) exécute n'importe quelle adresse pour arranger le fichier exécutable final, mais dans le cas des fonctions qui résident dans une DLL externe, le linker ne sait pas que ces adresses construisent un temps. Dans ce cas le linker utilise une bibliothèque d'importation pour la DLL (a appelé vaguement aussi le fichier .LIB). Une bibliothèque d'importation ne contient aucun code exécutable, elle nomme seulement les emplacements de fonctions exportées par la DLL. Le linker utilise les emplacements dans la bibliothèque d'importation pour résoudre des références aux fonctions de DLL externes du client.

En général, le chargement implicite est le meilleur cas quand un client exige toujours au moins une procédure dans la DLL, puisque Windows charge automatiquement la DLL du client. L'inconvénient de cette méthode est que si la DLL exigée est introuvable, Windows refusera d'exécuter votre application! Si vous chargez la DLL explicitement comme dans la méthode suivante et donc que la DLL non essentiel ne peut pas être trouvé, votre programme peut simplement informer l'utilisateur et continuer l'exécution.

Jonction explicite(la Durée d'exploitation)

Windows ne charge pas la DLL avant que le premier client ne le demande explicitement pendant l'exécution. Pour le faire, le client utilise les fonctions d'API suivantes :

LoadLibrary - Charge la DLL dans la mémoire
GetProcAddress - Obtenir l'adresse de chaque fonction exigée
FreeLibrary - Décharge la DLL quand les fonctions ne sont plus exigées

Bien que cela exige plus d'effort de programmation dans le client pour qu'il permette au contrôle que les DLL (si chacune) se chargent au temps exécuté. Un fichier de LIB pour la DLL n'est pas nécessaire bien que vous deviez toujours savoir combien de paramètres les fonctions prennent à l'intérieur de cela, etc. De plus vous pouvez appeler les fonctions non documentées qui ne sont pas détaillées dans les bibliothèques d'importation et sont souvent exportées par ordinal seulement.

Pour charger une DLL, implicitement ou explicitement, Windows cherche le fichier DLL dans les répertoires suivants dans l'ordre montré:

1. La liste d'adresses actuelle
2. La liste d'adresses de Windows, qui contient par exemple WIN.COM
3. La liste d'adresses du système de Windows, qui contient des fichiers du système comme GDI.EXE
4. La liste d'adresses où le programme du client réside
5. Répertoires inscrits dans le string d'environnement du CHEMIN
6. Répertoires qui dressent le plan d'un réseau

Si Windows ne localise pas la DLL dans n'importe lequel de ces répertoires, il avertit l'utilisateur avec une boîte de message.

Code de la DLL

Le code dans une DLL consiste à des fonctions exportées et non exportées. Les fonctions Exportées sont des routines publiques servant des clients tandis que les fonctions non exportées fournissent l'appui privé, interne pour les procédures exportées et ne sont pas visibles à un client. Les fonctions Exportées doivent être inscrites dans la section EXPORTATIONS d'une définition de module (le fichier .DEF) - un fichier texte contenant des informations utilisées par le linker. Nous en discuterons bientôt.

La Fonction DLLEntryPoint

Une DLL a d'habitude une fonction de point d'entrée comme indiqué ci-dessous (cela ne doit pas être appelé DLLENTRYPOINT, il peut être appelé autrement, comme eg. LibMain qui est généralement utilisé.) le système appelle la fonction de point d'entrée chaque fois qu'un processus ou un thread chargent ou déchargent la DLL. Il peut être utilisé pour exécuter l'initialisation simple ou des tâches de nettoyage.

Le système appelle la fonction de point d'entrée dans le contexte du processus ou du thread qui a fait que la fonction a été appelée. Cela permet à une DLL d'utiliser sa fonction de point d'entrée pour allouer la mémoire dans l'espace d'adresse virtuel du processus d'appel ou ouvrir des handles accessibles au processus. La fonction de point d'entrée peut aussi allouer la mémoire qui est privée à un nouveau thread en utilisant le thread du stockage local (TLS).

DLLENTRYPOINT typique y ressemble (voir Win32.hlp):

Autrement dit, quand Windows appelle votre DLLENTRYPOINT, il passe 3 paramètres mais le 3ème n'est pas utilisé :

hInstDLL est le handle du module de DLL. Ce n'est pas le même comme le cas de handle du processus et est utile si par exemple votre DLL a des ressources comme ceux-ci qui ont accès par le handle de DLL. Vous devriez garder cette valeur si vous deviez l'utiliser plus tard comme vous ne pouvez pas l'obtenir de nouveau facilement. La valeur ne change pas à moins que la DLL ne soit déchargée et chargée de nouveau donc elle est sûr d'être stocker dans une variable global.

fdwReason peut être une des quatre valeurs :

DLL_PROCESS_ATTACH - la DLL reçoit cette valeur quand elle est d'abord injectée dans le processus de l'adresse espace. Vous pouvez utiliser cette occasion de faire l'initialisation.

DLL_PROCESS_DETACH - la DLL reçoit cette valeur quand elle est déchargée du processus de l'adresse espace. Vous pouvez utiliser cette occasion de faire un certain nettoyage en désaffectant de la mémoire.

DLL_THREAD_ATTACH - la DLL reçoit cette valeur quand le processus crée un nouveau thread.

DLL_THREAD_DETACH - la DLL reçoit cette valeur quand un thread dans le processus est détruit.

Vous devez retourner VRAI dans eax si vous voulez que la DLL continue à fonctionner. Si vous retournez FAUX, la DLL ne sera pas chargée. Par exemple, si votre code d'initialisation doit allouer un peu de mémoire et qu'il ne peut pas le faire avec succès, la fonction d'entrypoint devrait retourner FAUX pour indiquer que la DLL ne peut pas fonctionner.

Pour des processus utilisant la jonction implicite (une fois chargée), une valeur de retour de causes FAUSSES à l'initialisation du processus le fait échouer et le processus se termine comme nous avons dit susdit. Pour des processus utilisant la jonction explicite (la durée d'exploitation), une valeur de retour de causes FAUSSES et c'est le LoadLibrary ou la fonction de LoadLibraryEx qui les font rendre un NUL, alors l'échec de l'indication et le système déchargent simplement la DLL.

D'autres Fonctions

Vous pouvez mettre vos fonctions n'importe où dans la DLL, mais si vous voulez qu'elles soient accessibles à d'autres modules, vous devez mettre leurs noms dans la liste d'exportation dans un fichier DEF comme nous avons dit susdit. Ensuite nous regarderons le code essentiel pour une DLL et son fichier DEF.

Le fichier ASM

Créez un nouveau projet dans WinAsm vide, mais choisissez cette fois le "Standard DLL":

Sauvez maintenant le projet et coller le code de DLLSKELETON.ASM dans WinAsm:

Si le DLLENTRYPOINT proc est seulement pour être appelé une fois quand la DLL est chargée, alors les conditions traitant les autres situations peuvent être omises et "vrai" peut être rendu immédiatement. Cela semble beaucoup plus simple - voir le Tutorial suivant par exemple.

Note: L'avancement soulignée pour nos noms de fonction exportés n'est pas nécessaire mais beaucoup de programmeurs l'ajoutent pour distinguer leurs fonctions propres de l'API.

Le fichier DEF

Créez ensuite un fichier texte vide dans le dossier de projet et rebaptisez-le DLLSKELETON.DEF puis dans WinAsm, cliquez sur le bouton pour Ajouter des Fichiers (voir la photo en dessous):

Double-cliquez sur le fichier de DLLskeleton.def et il devrait apparaître dans la barre d'explorateur de projet :

Maintenant WinAsm changera les options linker correctement pour l'utiliser et vous serez capables de l'éditer directement dans la fenêtre de code. Ajoutez les lignes suivantes :

LIBRARY DLLskeleton
EXPORTS _ExpFunc1 @ 01
_ExpFunc2 @ 02
_ExpFunc3 @ 03

Les mots de la BIBLIOTHÈQUE et des EXPORTATIONS sont des déclarations de définition de module. Il y a beaucoup d'entre eux détaillés dans la documentation masm61 mais ceux-ci sont les 2 seuls que nous devons connaître:

La déclaration de bibliothèque n'est pas en réalité exigée - notre projet sera toujours construit comme une DLL sans cela.

Notez s'il vous plaît le mot-clé NONAME ci-dessus pour exporter des fonctions par ordinal seulement. Nous l'utiliserons dans le Tutoriel suivant. Pour plus de renseignements sur des exportations et ordinals voir mon Tutorial PE. Notez que dans les commentaires, on inclus des fichiers qui ont la même syntaxe dans des fichiers de def que dans des fichiers asm.

Le fait de cliquer sur Go, tous les fichiers dont on a besoin vont se créer, non seulement la DLL, mais aussi un fichier LIB que vous pouvez utiliser pour avoir accès à vos fonctions par la jonction implicite (une fois chargée):

Dans le Tutorial suivant nous regarderons les différentes façons d'appeler des fonctions conservées dans une DLL.


ARTeam Tutorial

Exemple d'une DLL Simple

Nouveaux Concepts:

L'étude pour appeler des fonctions exportées par une DLL (Dynamic Link Library)

fichier contenant une bibliothèque de fonction utilisée par les programmeurs.

Nouvelles fonctions API:

LoadLibrary
GetProcAddress
FreeLibrary

Dans ce chapitre nous coderons une DLL simple avec des fonctions exportées et regarderons les façons différentes d'appeler la fonction de notre application client. Dans le premier exemple nous utiliserons la jonction implicite(une fois chargé) et dans le deuxième nous utiliserons la jonction explicite(la durée d'exploitation). Dans le deuxième cas nous importerons une des fonctions par ordinal. C'est la méthode que nous devons utiliser pour avoir accès aux API non documentées comme les noms de fonction n'apparaîtront pas dans la DLL ou de la bibliothèque d'importation disponible.

D'abord nous construirons une DLL pour exporter 2 fonctions simples - un de nom et l'autre par ordinal seulement. Créez un nouveau projet dans WinAsm et s'assurer que vous utilisez les fixations DLL dont on a déjà discutées dans le dernier Tutorial. Maintenant collez le code de MyDLL.asm dans WinAsm:

Ajoutez un fichier DEF avec les lignes suivantes:

EXPORTS

_ExpFunc1 @ 01
_ExpFunc2 @ 02 NONAME

Souvenez-vous dans le dernier Tutorial que le mot-clé NONAME arrête le nom de fonction étant placé dans la DLL et l'exporter par ordinal seulement. Cliquez Go pour créer les Fichiers MyDLL.LIB et MyDLL.DLL. Si vous le souhaitez, vous pouvez vérifier la DLL résultant avec n'importe quel PE Explorateur d'outils, par exemple CFF Explorer par Ntoskrnl:

Notez que la fonction avec la valeur ordinale 2 n'a aucune valeur dans les champs de nom et aussi que NumberOfFunctions est 1 fois plus grand que NumberOfNames.

Pour utiliser le fichier LIB pour lier une application avec notre nouvelle DLL au temps de charge, nous avons aussi besoin d'un fichier INC. Cela peut être fait avec l'utilitaire lib2inc, mais peut aussi être écrit à la main comme un fichier texte et renommer MyDLL.INC. Il contient simplement une liste des prototypes de toutes les fonctions exportées par la DLL et de la même manière nous écrivons des prototypes pour nos procédures propres. Pour utiliser lib2inc ouvrant une commande sans faute du dossier contenant le fichier lib, lib2inc et le type "lib2inc MyDLL.lib", voir l'image ci-dessous (sans citations) :

Cela créera un fichier INC simple comme décrit ci-dessus:

Maintenant nous coderons une application rapide pour lier notre DLL une fois chargée et importerons les fonctions. Lancez WinAsm et faites un nouveau projet - Standard exécutable cette fois-ci. Collez le code Implicit.asm dans WinAsm:

Cet exemple d'application appelle simplement les 2 fonctions l'une après l'autre et quitte ensuite. Notez bien que la deuxième fonction est exportée par ordinal seulement et que le nom n'est pas incorporé dans la DLL, donc nous pouvons toujours l'appeler de nom(ex: include MyDLL.inc, include MyDLL.lib, etc...) parce que nous avons les fichiers LIB et INC.

Maintenant nous changerons la susdit application pour qu'elle utilise la jonction de durée d'exploitation. La première fonction sera appelée de nom et la deuxième par ordinal. Créez un nouveau projet et puis coller le code de Explicit.asm dans WinAsm:

Dans ce cas nous utilisons LoadLibrary pour charger notre DLL dans le processus adresse de l'espace en rendant son handle, puis GetProcAddress pour trouver l'adresse des fonctions que nous voulons appeler. GetProc peut utiliser un indicateur sur le nom de la fonction ou la valeur ordinale de la fonction.

Notez que nous n'avons pas besoin d'un lib ou d'un fichier inc, mais si la fonction appelée avait eu besoin des paramètres spécifiques, nous devrions connaître ceux pour l'utiliser correctement.

Finalement FreeLibrary est utilisé pour décharger la DLL. Cela arriverait en réalité automatiquement après l'appel à ExitProcess mais c'est la bonne pratique de codage pour le faire de toute façon.


µDico MASM Flottant 4.4

DllEntryPoint

The DllEntryPoint function is an optional method of entry into a dynamic-link library (DLL).
If the function is used, it is called by the system when processes and threads are initialized and terminated, or upon calls to
the LoadLibrary and FreeLibrary functions.
DllEntryPoint is a placeholder for the library-defined function name.
The actual namemust be specified at build time. For more information, see the documentation included with your development tools.

BOOL WINAPI DllEntryPoint(

    HINSTANCE hinstDLL, 	  // handle to DLL module
    DWORD fdwReason, 	  // reason for calling function
    LPVOID lpvReserved  	  // reserved
   );
Parameters

hinstDLL

A handle to the DLL. The value is the base address of the DLL. The HINSTANCE of a DLL is the same as the HMODULE of the DLL, so hinstDLL can be used in subsequent calls
to the GetModuleFileName function and other functions that require a module handle.

fdwReason

Specifies a flag indicating why the DLL entry-point function is being called. This parameter can be one of the following values:

Value	                      Meaning

DLL_PROCESS_ATTACH

Indicates that the DLL is attaching to the address space of the current process as a result of the process starting up or as a result of a call to LoadLibrary. DLLs can
use this opportunity to initialize any instance data or to use the TlsAlloc function to allocate a thread local storage (TLS) index.
During initial process startup or after a call to LoadLibrary, the operating system scans the list of loaded DLLs for the process. For each DLL that has not already been
called with the DLL_PROCESS_ATTACH value, the system calls the DLL’s entry-point function. This call is made in the context of the thread that caused the process address
space to change, such as the primary thread of the process or the thread that called LoadLibrary.

DLL_THREAD_ATTACH

Indicates that the current process is creating a new thread. When this occurs, the system calls the entry-point function of all DLLs currently attached to the process.
The call is made in the context of the new thread. DLLs can use this opportunity to initialize a TLS slot for the thread. A thread calling the DLL entry-point function with the
DLL_PROCESS_ATTACH value does not call the DLL entry-point function with the DLL_THREAD_ATTACH value.
Note that a DLL’s entry-point function is called with this value only by threads created after the DLL is attached to the process. When a DLL is attached by LoadLibrary,
existing threads do not call the entry-point function of the newly loaded DLL.

DLL_THREAD_DETACH

Indicates that a thread is exiting cleanly. If the DLL has stored a pointer to allocated memory in a TLS slot, it uses this opportunity to free the memory. The operating system
calls the entry-point function of all currently loaded DLLs with this value. The call is made in the context of the exiting thread. There are cases in which the entry-point
function is called for a terminating thread even if the DLL never attached to the thread ¾ for example, the entry-point function was never called with
the DLL_THREAD_ATTACH value in the context of the thread in either of these two situations:·	The thread was the initial thread in the process, so the system called the entry-point
function with the DLL_PROCESS_ATTACH value. ·	The thread was already running when a call to the LoadLibrary function was made,
so the system never called the entry-point function for it.

DLL_PROCESS_DETACH

Indicates that the DLL is detaching from the address space of the calling process as a result of either a clean process exit or of a call to FreeLibrary. The DLL can use this
opportunity to call the TlsFree function to free any TLS indices allocated by using TlsAlloc and to free any thread local data. When a DLL detaches from a process as a result
of process termination or as a result of a call to FreeLibrary, the operating system does not call the DLL’s entry-point function with the DLL_THREAD_DETACH
value for the individual threads of the process. The DLL is only given DLL_PROCESS_DETACH notification. DLLs can take this opportunity to clean up all resources for all
threads attached and known to the DLL.
lpvReserved

Specifies further aspects of DLL initialization and cleanup.
If fdwReason is DLL_PROCESS_ATTACH, lpvReserved is NULL for dynamic loads and non-NULL for static loads.
If fdwReason is DLL_PROCESS_DETACH, lpvReserved is NULL if DllEntryPoint has been called by using FreeLibrary and non-NULL if DllEntryPoint has been called during process termination.

Return Values

When the system calls the DllEntryPoint function with the DLL_PROCESS_ATTACH value, the function returns TRUE if it succeeds or FALSE if initialization fails. If the return value
is FALSE when DllEntryPoint is called because the process uses the LoadLibrary function, LoadLibrary returns NULL. If the return value is FALSE when DllEntryPoint is called
during process initialization, the process terminates with an error. To get extended error information, call GetLastError.

When the system calls the DllEntryPoint function with any value other than DLL_PROCESS_ATTACH, the return value is ignored.

See Also

FreeLibrary, GetModuleFileName, LoadLibrary, TlsAlloc, TlsFree


µDico MASM Flottant 4.4

LoadLibrary

The LoadLibrary function maps the specified executable module into the address space of the calling process.

HINSTANCE LoadLibrary(

    LPCTSTR lpLibFileName 	// address of filename of executable module
   );
Parameters

lpLibFileName

Points to a null-terminated string that names the executable module (either a .DLL or .EXE file). The name specified is the filename of the module and is not related to the name stored in the library module itself, as specified by the LIBRARY keyword in the module-definition (.DEF) file.

If the string specifies a path but the file does not exist in the specified directory, the function fails.
If a path is not specified and the filename extension is omitted, the default library extension .DLL is appended. However, the filename string can include a trailing point character (.) to indicate that the module name has no extension. When no path is specified, the function searches for the file in the following sequence:

1.	The directory from which the application loaded.
    2.	The current directory.
    3.	Windows 95: The Windows system directory. Use the GetSystemDirectory function to get the path of this directory.

Windows NT: The 32-bit Windows system directory. Use the GetSystemDirectory function to get the path of this directory. The name of this directory is SYSTEM32.

4.	Windows NT: The 16-bit Windows system directory. There is no Win32 function that obtains the path of this directory, but it is searched. The name of this directory is SYSTEM.
    5.	The Windows directory. Use the GetWindowsDirectory function to get the path of this directory.
    6.	The directories that are listed in the PATH environment variable.

The first directory searched is the one directory containing the image file used to create the calling process (for more information, see the CreateProcess function). Doing this allows private dynamic-link library (DLL) files associated with a process to be found without adding the process’s installed directory to the PATH environment variable.
Once the function obtains a fully qualified path to a library module file, the path is compared (case independently) to the full paths of library modules currently loaded into the calling process. These libraries include those loaded when the process was starting up as well as those previously loaded by LoadLibrary but not unloaded by FreeLibrary. If the path matches the path of an already loaded module, the function just increments the reference count for the module and returns the module handle for that library.

Return Values

If the function succeeds, the return value is a handle to the module.
If the function fails, the return value is NULL. To get extended error information, call GetLastError.

Remarks

LoadLibrary can be used to map a DLL module and return a handle that can be used in GetProcAddress to get the address of a DLL function. LoadLibrary can also be used to map other executable modules. For example, the function can specify an .EXE file to get a handle that can be used in FindResource or LoadResource.
Module handles are not global or inheritable. A call to LoadLibrary by one process does not produce a handle that another process can use ¾ for example, in calling GetProcAddress. The other process must make its own call to LoadLibrary for the module before calling GetProcAddress.

If the module is a DLL not already mapped for the calling process, the system calls the DLL’s DllEntryPoint function with the DLL_PROCESS_ATTACH value. If the DLL’s entry-point function does not return TRUE, LoadLibrary fails and returns NULL.
Windows 95: If you are using LoadLibrary to load a module that contains a resource whose numeric identifier is greater than 0x7FFF, LoadLibrary fails.

See Also

DllEntryPoint, FindResource, FreeLibrary, GetProcAddress, GetSystemDirectory, GetWindowsDirectory, LoadResource


µDico MASM Flottant 4.4

FreeLibrary

The FreeLibrary function decrements the reference count of the loaded dynamic-link library (DLL) module.
When the reference count reaches zero, the module is unmapped from the address space of the calling process and the handle is no longer valid.
This function supersedes the FreeModule function.

BOOL FreeLibrary(

    HMODULE hLibModule 	   // handle to loaded library module
   );
Parameters

hLibModule

Identifies the loaded library module. The LoadLibrary or GetModuleHandle function returns this handle.

Return Values

If the function succeeds, the return value is nonzero.
If the function fails, the return value is zero. To get extended error information, call GetLastError.

Remarks

Each process maintains a reference count for each loaded library module. This reference count is incremented each time LoadLibrary is called and is decremented each time FreeLibrary is called. A DLL module loaded at process initialization due to load-time dynamic linking has a reference count of one. This count is incremented if the same module is loaded by a call to LoadLibrary.
Before unmapping a library module, the system enables the DLL to detach from the process by calling the DLL’s DllEntryPoint function, if it has one, with the DLL_PROCESS_DETACH value. Doing so gives the DLL an opportunity to clean up resources allocated on behalf of the current process. After the entry-point function returns, the library module is removed from the address space of the current process.

Calling FreeLibrary does not affect other processes using the same library module.

See Also

DllEntryPoint, FreeLibraryAndExitThread, FreeModule, GetModuleHandle, LoadLibrary


µDico MASM Flottant 4.4

GetProcAddress

The GetProcAddress function returns the address of the specified exported dynamic-link library (DLL) function.

FARPROC GetProcAddress(

    HMODULE hModule,	// handle to DLL module
    LPCSTR lpProcName 	// name of function
   );
Parameters

hModule

Identifies the DLL module that contains the function. The LoadLibrary or GetModuleHandle function returns this handle.

lpProcName

Points to a null-terminated string containing the function name, or specifies the function’s ordinal value. If this parameter is an ordinal value, it must be in the low-order word; the high-order word must be zero.

Return Values

If the function succeeds, the return value is the address of the DLL’s exported function.
If the function fails, the return value is NULL. To get extended error information, call GetLastError.

Remarks

The GetProcAddress function is used to retrieve addresses of exported functions in DLLs.
The spelling and case of the function name pointed to by lpProcName must be identical to that in the EXPORTS statement of the source DLL’s module-definition (.DEF) file.
The lpProcName parameter can identify the DLL function by specifying an ordinal value associated with the function in the EXPORTS statement. GetProcAddress verifies that the specified ordinal is in the range 1 through the highest ordinal value exported in the .DEF file. The function then uses the ordinal as an index to read the function’s address from a function table. If the .DEF file does not number the functions consecutively from 1 to N (where N is the number of exported functions), an error can occur where GetProcAddress returns an invalid, non-NULL address, even though there is no function with the specified ordinal.

In cases where the function may not exist, the function should be specified by name rather than by ordinal value.

See Also

FreeLibrary, GetModuleHandle, LoadLibrary


µDico MASM Flottant 4.4

GetModuleFileName

The GetModuleFileName function retrieves the full path and filename for the executable file containing the specified module.

Windows 95: The GetModuleFilename function will return long filenames when an application’s version number is greater than or equal to 4.00 and
the long filename is available. Otherwise, it returns only 8.3 format filenames.

DWORD GetModuleFileName(

    HMODULE hModule,	   // handle to module to find filename for
    LPTSTR lpFilename,       // pointer to buffer for module path
    DWORD nSize       	   // size of buffer, in characters
   );
Parameters

hModule

Identifies the module whose executable filename is being requested. If this parameter is NULL, GetModuleFileName returns the path for the file used to create the calling process.

lpFilename

Points to a buffer that is filled in with the path and filename of the given module.

nSize

Specifies the length, in characters, of the lpFilename buffer. If the length of the path and filename exceeds this limit, the string is truncated.

Return Values

If the function succeeds, the return value is the length, in characters, of the string copied to the buffer.
If the function fails, the return value is zero. To get extended error information, call GetLastError.

Remarks

If a module is loaded in two processes, its module filename in one process may differ in case from its module filename in the other process.

See Also

GetModuleHandle, LoadLibrary


µDico MASM Flottant 4.4

FindResource

The FindResource function determines the location of a resource with the specified type and name in the specified module.

HRSRC FindResource(

    HMODULE hModule,       // resource-module handle
    LPCTSTR lpName,	        // pointer to resource name
    LPCTSTR lpType      	 // pointer to resource type
   );
Parameters

hModule

A handle to the module whose executable file contains the resource.
A value of NULL specifies the module handle associated with the image file that the operating system used to create the current process.

lpName

Specifies the name of the resource. For more information, see the Remarks section.

lpType

Specifies the resource type. For more information, see the Remarks section. For standard resource types, this parameter can be one of the following values:

Value	Meaning
RT_ACCELERATOR	Accelerator table
RT_ANICURSOR	Animated cursor
RT_ANIICON	Animated icon
RT_BITMAP	Bitmap resource
RT_CURSOR	Hardware-dependent cursor resource
RT_DIALOG	Dialog box
RT_FONT	Font resource
RT_FONTDIR	Font directory resource
RT_GROUP_CURSOR	Hardware-independent cursor resource
RT_GROUP_ICON	Hardware-independent icon resource
RT_ICON	Hardware-dependent icon resource
RT_MENU	Menu resource
RT_MESSAGETABLE	Message-table entry
RT_RCDATA	Application-defined resource (raw data)
RT_STRING	String-table entry
RT_VERSION	Version resource
Return Values

If the function succeeds, the return value is a handle to the specified resource’s info block. To obtain a handle to the resource, pass this handle to the LoadResource function.
If the function fails, the return value is NULL. To get extended error information, call GetLastError.

Remarks

If the high-order word of the lpName or lpType parameter is zero, the low-order word specifies the integer identifier of the name or type of the given resource. Otherwise, those parameters are long pointers to null-terminated strings. If the first character of the string is a pound sign (#), the remaining characters represent a decimal number that specifies the integer identifier of the resource’s name or type. For example, the string “#258” represents the integer identifier 258.

An application should reduce the amount of memory required for the resources by referring to them by integer identifier instead of by name.
An application can use FindResource to find any type of resource, but this function should be used only if the application must access the binary resource data when making subsequent calls to LoadLibrary and LockResource.
To use a resource immediately, an application should use one of the following resource-specific functions to find and load the resources in one call:

Function	Action
FormatMessage	Loads and formats a message-table entry.
LoadAccelerators	Loads an accelerator table.
LoadBitmap	Loads a bitmap resource.
LoadCursor	Loads a cursor resource.
LoadIcon	Loads an icon resource.
LoadMenu	Loads a menu resource.
LoadString	Loads a string-table entry.
For example, an application can use the LoadIcon function to load an icon for display on the screen. However, the application should use FindResource and LoadResource if it is loading the icon to copy its data to another application.

See Also

FindResourceEx, FormatMessage, LoadAccelerators, LoadBitmap, LoadCursor, LoadIcon, LoadMenu, LoadResource, LoadString, LockResource, SizeofResource


µDico MASM Flottant 4.4

FindResourceEx

The FindResourceEx function determines the location of the resource with the specified type, name, and language in the specified module.

HRSRC FindResourceEx(

    HMODULE hModule,	// resource-module handle
    LPCTSTR lpType,	      // pointer to resource type
    LPCTSTR lpName,	      // pointer to resource name
    WORD wLanguage 	      // resource language
   );
Parameters

hModule

Identifies the module whose executable file contains the resource. If this parameter is NULL, the function searches the module used to create the current process.

lpType

Points to a null-terminated string specifying the type name of the resource. For more information, see the Remarks section. For standard resource types, this parameter can be one of the following values:

Value	Meaning
RT_ACCELERATOR	Accelerator table
RT_ANICURSOR	Animated cursor
RT_ANIICON	Animated icon
RT_BITMAP	Bitmap resource
RT_CURSOR	Hardware-dependent cursor resource
RT_DIALOG	Dialog box
RT_FONT	Font resource
RT_FONTDIR	Font directory resource
RT_GROUP_CURSOR	Hardware-independent cursor resource
RT_GROUP_ICON	Hardware-independent icon resource
RT_ICON	Hardware-dependent icon resource
RT_MENU	Menu resource
RT_MESSAGETABLE	Message-table entry
RT_RCDATA	Application-defined resource (raw data)
RT_STRING	String-table entry
RT_VERSION	Version resource
lpName

Points to a null-terminated string specifying the name of the resource. For more information, see the Remarks section.

wLanguage

Specifies the language of the resource. If this parameter is MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), the current language associated with the calling thread is used.
To specify a language other than the current language, use the MAKELANGID macro to create this parameter. For more information, see the MAKELANGID macro.

Return Values

If the function succeeds, the return value is a handle to the specified resource’s info block. To obtain a handle to the resource, pass this handle to the LoadResource function.
If the function fails, the return value is NULL. To get extended error information, call GetLastError.

Remarks

If the high-word of the lpType or lpName parameter is zero, the low-word specifies the integer identifier of the type or name of the given resource. Otherwise, those parameters are pointers to null-terminated strings. If the first character of the string is a pound sign (#), the remaining characters represent a decimal number that specifies the integer identifier of the resource’s name or type. For example, the string “#258” represents the integer identifier 258.

Applications should reduce the amount of memory required for the resources by referring to them by integer identifier instead of by name.
An application can use FindResource to find any type of resource, but this function should be used only if the application must access the binary resource data when making subsequent calls to the LoadLibrary and LockResource functions.
To use a resource immediately, an application should use the following resource-specific functions to find and load the resources in one call:

Function	Action
FormatMessage	Loads and formats a message-table entry.
LoadAccelerators	Loads an accelerator table.
LoadBitmap	Loads a bitmap resource.
LoadCursor	Loads a cursor resource.
LoadIcon	Loads an icon resource.
LoadMenu	Loads a menu resource.
LoadString	Loads a string-table entry.
For example, an application can use the LoadIcon function to load an icon for display on the screen. However, the application should use FindResource and LoadResource if it is loading the icon to copy its data to another application.

See Also

FindResource, FormatMessage, LoadAccelerators, LoadBitmap, LoadCursor, LoadIcon, LoadMenu, LoadString, LoadResource, MAKELANGID


µDico MASM Flottant 4.4

LoadResource

The LoadResource function loads the specified resource into global memory.

HGLOBAL LoadResource(

    HMODULE hModule,	    // resource-module handle
    HRSRC hResInfo 	          // resource handle
   );
Parameters

hModule

Identifies the module whose executable file contains the resource. If hModule is NULL, Windows loads the resource from the module that was used to create the current process.

hResInfo

Identifies the resource to be loaded. This handle must be created by using the FindResource or FindResourceEx function.

Return Values

If the function succeeds, the return value is a handle to the global memory block containing the data associated with the resource.
If the function fails, the return value is NULL. To get extended error information, call GetLastError.

Remarks

Both Windows 95 and Windows NT automatically free resources. You do not need to call the FreeResource function to free a resource loaded by using the LoadResource function.

See Also

FindResource, FindResourceEx, FreeResource, LoadLibrary, LoadModule, LockResource


µDico MASM Flottant 4.4

GetSystemDirectory

The GetSystemDirectory function retrieves the path of the Windows system directory.
The system directory contains such files as Windows libraries, drivers, and font files.

UINT GetSystemDirectory(

    LPTSTR lpBuffer,  	  // address of buffer for system directory
    UINT uSize        	  // size of directory buffer
   );
Parameters

lpBuffer

Points to the buffer to receive the null-terminated string containing the path. This path does not end with a backslash unless the system directory is the root directory. For example, if the system directory is named WINDOWS\SYSTEM on drive C, the path of the system directory retrieved by this function is C:\WINDOWS\SYSTEM.

uSize

Specifies the maximum size of the buffer, in characters. This value should be set to at least MAX_PATH.

Return Values

If the function succeeds, the return value is the length, in characters, of the string copied to the buffer, not including the terminating null character. If the length is greater than the size of the buffer, the return value is the size of the buffer required to hold the path.
If the function fails, the return value is zero. To get extended error information, call GetLastError.

Remarks

Applications should not create files in the system directory. If the user is running a shared version of Windows, the application does not have write access to the system directory. Applications should create files only in the directory returned by the GetWindowsDirectory function.

See Also

GetCurrentDirectory, GetWindowsDirectory, SetCurrentDirectory


µDico MASM Flottant 4.4

GetWindowsDirectory

The GetWindowsDirectory function retrieves the path of the Windows directory.
The Windows directory contains such files as Windows-based applications, initialization files, and Help files.

UINT GetWindowsDirectory(

    LPTSTR lpBuffer,	   // address of buffer for Windows directory
    UINT uSize         	   // size of directory buffer
   );
Parameters

lpBuffer

Points to the buffer to receive the null-terminated string containing the path. This path does not end with a backslash unless the Windows directory is the root directory. For example, if the Windows directory is named WINDOWS on drive C, the path of the Windows directory retrieved by this function is C:\WINDOWS. If Windows was installed in the root directory of drive C, the path retrieved is C:\.

uSize

Specifies the maximum size, in characters, of the buffer specified by the lpBuffer parameter. This value should be set to at least MAX_PATH to allow sufficient room in the buffer for the path.

Return Values

If the function succeeds, the return value is the length, in characters, of the string copied to the buffer, not including the terminating null character.
If the length is greater than the size of the buffer, the return value is the size of the buffer required to hold the path.
If the function fails, the return value is zero. To get extended error information, call GetLastError.

Remarks

The Windows directory is the directory where an application should store initialization and help files. If the user is running a shared version of Windows, the Windows directory is guaranteed to be private for each user.
If an application creates other files that it wants to store on a per-user basis, it should place them in the directory specified by the HOMEPATH environment variable. This directory will be different for each user, if so specified by an administrator, via the User Manager administrative tool. HOMEPATH always specifies either the user’s home directory, which is guaranteed to be private for each user, or a default directory (for example, C:\USERS\DEFAULT) where the user will have all access.

See Also

GetCurrentDirectory, GetSystemDirectory


Untitled Document

GIFLIB
v1.0
Introduction
Tutorial
Functions
Structures
Constants
About


Untitled Document

About

Introduction

GIFLIB is an easy to use library that enables you to use the GIF file format in your programs. It comes as a static library which can be linked to your program. GIFLIB is very small. This documentation is focussed on win32asm programming, if you want to use it with other languages, you'll have to convert the include files. GIFLIB is focussed on MASM users.

Specifications

Supported

  • 2, 4, 8, 16, 32, 64, 128 and 256 color gifs
  • Outputting data as 1-bit, 4-bit and 8-bit data
  • Global or local color tables
  • Exporting the color table to RGBQUAD format
  • Useful functions for implementing with the windows GDI

Not (yet) supported

  • Interlaced gifs
  • Non-image drawing extensions (plain text extension)
  • Graphic control extensions (transparency, etc)
  • All other things not mentioned under the supported section ;-)

More

  • GIFLIB is entirely written in assembly language (win32asm)
  • GIFLIB is very small (around 5kb)
  • Your programs will grow just about 2kb when using the library
  • GIFLIB is free!

Licensing

GIFLIB is © copyrighted by Exagone as freeware. This library is entirely free to use, you may use it in your programs and distribute your programs freely. Furthermore, the general public license apllies to this software. By using this software (documentation, library or source code), you agree to the terms in this license.

Additionally, the official GIF89a specification which applies for this library declares:

The Graphics Interchange Format(c) is the copyright property of CompuServe
Incorporated. Only CompuServe Incorporated is authorized to define, redefine,
enhance, alter, modify or change in any way the definition of the format.

CompuServe Incorporated hereby grants a limited, non-exclusive, royalty-free
license for the use of the Graphics Interchange Format(sm) in computer
software; computer software utilizing GIF(sm) must acknowledge ownership of the
Graphics Interchange Format and its Service Mark by CompuServe Incorporated, in
User and Technical Documentation. Computer software utilizing GIF, which is
distributed or may be distributed without User or Technical Documentation must
display to the screen or printer a message acknowledging ownership of the
Graphics Interchange Format and the Service Mark by CompuServe Incorporated; in
this case, the acknowledgement may be displayed in an opening screen or leading
banner, or a closing screen or trailing banner. A message such as the following
may be used:

"The Graphics Interchange Format(c) is the Copyright property of
CompuServe Incorporated. GIF(sm) is a Service Mark property of
CompuServe Incorporated."

For further information, please contact :

CompuServe Incorporated
Graphics Technology Department
5000 Arlington Center Boulevard
Columbus, Ohio 43220
U. S. A.


Untitled Document

Constants

Currently, there are only three constants:

Image output formats

IMAGEFORMAT_1BIT | 1-bit, monochrome image
IMAGEFORMAT_4BIT | 4-bit, 16 color image
IMAGEFORMAT_8BIT | 8-bit, 256 color image


Untitled Document

Functions

GIFLoadHeader

GIFLoadHeader lpGifInfo:DWORD

function

Initializes the GIF decoder. This function should be called once for each gif that is decoded, The GIF_INFO structure is prepared for using GIFDecompress.

parameters

lpGifInfo Pointer to a GIF_INFO Structure that defines the GIF to use. The lpGIFData member of the structure should point to the raw GIF image data, the dwGIFDataSize member is the size of the data pointed to by lpGIFData

return value

The function returns TRUE if successful, FALSE if it failed. The GIFVersion, dwLSWidth and dwLSHeight members of the GIF_INFO are filled by the structure.

GIFInitalizeDecoder

GIFInitalizeDecoder lpGifInfo:DWORD

function

Initializes the GIF decoder. This function should be called once for each gif that is decoded, The GIF_INFO structure is prepared for using GIFDecompress. Always use GIFLoadHeader before GIFInitializeDecoder

parameters

lpGifInfo Pointer to a GIF_INFO Structure that defines the GIF to use. The structure should be initialized by GIFLoadHeader

return values

Returns TRUE if successful, FALSE if it failed. The following members of the GIF_INFO structure are initialized by the function:

lpColorTable | Pointer to the GIF's color table (RR GG BB format)
ColorCount | Number of colors in the GIF image
dwImageWidth | Width of the GIF Image
dwImageHeight | Height of the GIF Image

GIFGetColorTable

GIFGetColorTable lpGifInfo:DWORD, lpBuffer:DWORD

function

Retrieves the colortable to a given buffer as an array of RGBQUAD structures. Use this function for compatibility with the windows GDI functions.

parameters

lpGifInfo | Pointer to a GIF_INFO Structure that defines the GIF to use. The structure should be initialized by GIFLoadHeader and GIFInitializeDecoder.
lpBuffer | Points to a buffer that receives the color table as an array of RGBQUADs

remarks

The size of lpBuffer can be found by multiplying the number of colors in the color table by the size of a RGBQUAD structure (=4 bytes):
SizeOf(lpBuffer) = GifInfo.ColorCount * 4

return value

This function does not return a value.

GIFGetMinimumColorType

GIFGetMinimumColorType lpGifInfo:DWORD

function

Retrieves the output color type that ensures the smallest possible output format.

parameters

lpGifInfo Pointer to a GIF_INFO Structure that defines the GIF to use. The structure should be initialized by GIFLoadHeader and GIFInitializeDecoder.

return value

This function returns IMAGEFORMAT_1BIT, IMAGEFORMAT_4BIT or IMAGEFORMAT_8BIT. You can use the return value as output format when decoding the GIF. GIFGetMinimumColorType ensures that this output format gives the smallest output data. You can always use a higher output format (i.e. higher nr of bits), lower is not supported.

GIFGetOutputSize

GIFGetOutputSize lpGifInfo:DWORD

function

Retrieves the number of bytes to reserve for the output data.

parameters

lpGifInfo Pointer to a GIF_INFO Structure that defines the GIF to use. The structure should be initialized by GIFLoadHeader and GIFInitializeDecoder.

return value

The return value indicates the number of bytes that should be allocated to fit the output data size.

GIFDecompress

GIFDecompress lpGifInfo:DWORD

function

This is the main decompression function. It decompresses the GIF Image to the buffer pointed to by the lpImageData member of the GIF_INFO structure, in the format defined by the dwImageFormat member.

parameters

lpGifInfo Pointer to a GIF_INFO Structure that defines the GIF to use. The structure should be initialized by GIFLoadHeader and GIFInitializeDecoder.

return value

The function returns TRUE if successful.

GIFCleanup

GIFCleanup lpGifInfo:DWORD

function

Cleans the memory that is used internally by GIFLIB. This function should be called after using the decoder.

parameters

lpGifInfo Pointer to a GIF_INFO Structure.

return value

This function does not return a value.

GIFFillBitmapInfoStruct

GIFFillBitmapInfoStruct lpGifInfo:DWORD, lpBitmapInfoHeader:DWORD

function

GIFFillBitmapInfoStruct fills a BITMAPINFOHEADER with the right values from a GIF_INFO Structure.

parameters

lpGifInfo | Pointer to a GIF_INFO Structure. The dwImageWidth, dwImageHeight and dwImageFormat of this structure should have the right values. These values are set by GIFInitializeDecoder.
lpBitmapInfoHeader | Pointer to a BITMAPINFOHEADER structure to be filled in.

return value

This function does not return a value.

GIFLoadResource

GIFLoadResource lpGifInfo:DWORD, hInstance:DWORD, lpName:DWORD

function

Loads a GIF in the form of a resource of the RT_RCDATA type, and sets the lpGIFData and dwGIFDataSize members of the GIF_INFO structure.

parameters

lpGifInfo | Pointer to a GIF_INFO Structure. The lpGIFData and dwGIFDataSize members of the structure are set by GIFLoadResource
lpBitmapInfoHeader | Identifies the module whose executable file contains the resource.
lpName | Specifies the name of the resource or, if the high word of the parameter is NULL, a resource ID.

return value

This function does not return a value.

GIFLoadFile

GIFLoadFile lpGifInfo:DWORD, lpFile:DWORD

function

Loads a GIF-File from harddisk for use with the library.
NOTE: The lpGIFData member of the GIF_INFO structure is set to a pointer to the data in the file. You should free this memory using GlobalFree if the data is not needed anymore.

parameters

lpGifInfo | Pointer to a GIF_INFO Structure. The lpGIFData and dwGIFDataSize members of the structure are set by GIFLoadFile.
lpFile | Points to a string that specifies the filename of the GIF to be loaded

return value

This function does not return a value.


Untitled Document

GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you".

Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does.

1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program.

You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee.

2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions:

a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change.

b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License.

c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.)

These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it.

Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program.

In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License.

3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following:

a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,

b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,

c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.)

The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable.

If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code.

4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.

5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it.

6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License.

7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program.

If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances.

It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice.

This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License.

8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License.

9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.

Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation.

10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally.

NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

END OF TERMS AND CONDITIONS


Untitled Document

GIFLIB v1.0

This is the documentation of GIFLIB, version 1.0. The menu on the left contains documentation for various parts of the library.
See about for more information about distributing and using the library.

(C) Copyright 2000 by Exagone. http://exagone.cjb.net.


Untitled Document

Structures

In version 1.0, there's only one structure: GIF_INFO. It is used by every function in the GIFLIB. You should have one structure for each GIF you decode.

GIF_INFO STRUCT
GIFVersion db 3 dup(?)
dwLSWidth dd ?
dwLSHeight dd ?
lpColorTable dd ?
ColorCount dd ?
dwImageWidth dd ?
dwImageHeight dd ?
lpImageData dd ?
dwImageFormat dd ?
lpGIFData dd ?
dwGIFDataSize dd ?
InternalData db 44 dup (?)
GIF_INFO ENDS

GIFVersion | These are the second 3 bytes of the file, that indicate the version number of the GIF File, like "87a" or "89a".
dwLSWidth | Logical screen width (see GIF format)
dwLSHeight | Logical screen height (see GIF format)
lpColorTable | Pointer to the GIF color table (RR GG BB RR GG BB format)
ColorCount | Number of colors in the GIF
dwImageWidth | Actual image width (use this one to get the image dimensions)
dwImageHeight | Actual image height (use this one to get the image dimensions)
lpImageData | Output buffer for decoded data (in the format specified by dwImageFormat). Data is outputted as windows bitmap data, in one of the following three formats: 1-bit monochrome, 4-bit 16-color or 8-bit 256 color. Scanlines are aligned to dwords (see the bitmap documentation in the win32 programmer's reference)
dwImageFormat | Specifies the image format (IMAGEFORMAT_XXXX constants). You can use a format that has a higher maximum number of colors than the original GIF, but lower is not supported (256 color output from a 16 color gif is allowed, 16 color output from a 256 color gif is NOT).
lpGIFData | Points to the actual GIF data (in the GIF file format).
dwGIFDataSize | Size of the data pointed to by lpGIFData
InternalData | Data that is used internally by GIFLIB.


Untitled Document

GIFLIB v1.0 Documentation


Untitled Document

Tutorial

This is a quick start guide to the library.

Step 1 - GIF Data

First of all, you will need to specify what GIF data to use. This is defined by the lpGIFData and dwGIFDataSize members of the GIF_INFO structure. Create a new GIF_INFO structure:

.data?
TestGif GIF_INFO <>

Now you will have to fill the lpGIFData and dwGIFDataSize members of this structure. Set the values manually to some pointer, use GIFLoadResource or GIFLoadFile:

;manually
push somepointertogifdata
pop TestGif.lpGIFData
push sizeofsomegifdata
pop TestGif.dwDataSize

;or with GIFLoadResource (loads GIF data from a resource)
invoke GetModuleHandle, NULL
mov hInstance, eax
invoke GIFLoadResource, ADDR TestGif, hInstance, ID_GIFRESOURCE

;or width GIFLoadFile (loads GIF data from a file)
.data
GifFilename db "testimage.gif",0
.code
invoke GIFLoadFile, ADDR TestGif, ADDR GifFilename

Step 2 - Load header

Now load the GIF header width GIFLoadHeader

invoke GIFLoadHeader, ADDR TestGif

The GIFVersion, dwLSWidth and dwLSHeight members of TestGif are now set. This function should be called before using GIFInitializeDecoder or GIFDecompress.

Step 3 - Initialize decoder

Before using the decoder, you should initialize it:

invoke GIFInitializeDecoder, ADDR TestGif

The dwImageWidth, dwImageHeight, lpColorTable and ColorCount members of TestGif are set. This function should be called before using GIFDecompress.

Step 4 - Set image format etc.

You will now need to set an image format and an output buffer. The image format (dwImageFormat) can be IMAGEFORMAT_1BIT, IMAGEFORMAT_4BIT or IMAGEFORMAT_8BIT. A higher color format than the original gif may be used, lower is not allowed (see functions for more information). You can set the color format manually or use the minimum (i.e. the one that creates the smallest output) color format by using the return value of GIFGetMinimumColorType:

;manually
mov TestGif.dwImageFormat, IMAGEFORMAT_4BIT

;or automatically
invoke GIFGetMinimumColorType, ADDR TestGif
mov TestGif.dwImageFormat, eax

If you want to create a bitmap from it, use GIFFillBitmapInfoStruct to get a valid BITMAPINFOHEADER structure. Note that if you use a function like CreateDIBitmap, the color table should immediatly follow the BITMAPINFOHEADER structure (BITMAPINFO format).

.data?
BitmapInfoHeader BITMAPINFOHEADER <>
.code
invoke GIFFillBitmapInfoStruct, ADDR TestGif, ADDR BitmapInfoHeader

Now set the lpImageData member to a valid pointer to a buffer that's big enough to store the bitmap image (If you are using CreateDIBitmap, this buffer is created by windows for you). You can get the nr of bytes needed with GIFGetOutputSize:

invoke GIFGetOutputSize, ADDR TestGif
; ......
; create a buffer with size eax
; ......
mov TestGif.lpImageData, {pointer to buffer}

Step 5 - Decompress

GIFDecompress will decode the GIF with all the given parameters in the GIF_INFO structure:

invoke GIFDecompress, ADDR TestGif

Step 6- Cleanup

As the final step, cleanup the allocated memory:

invoke GIFCleanup, ADDR TestGif


ASM Mode Reel

    Cover Sheet for the GIF89a Specification

    DEFERRED CLEAR CODE IN LZW COMPRESSION

    There has been confusion about where clear codes can be found in the
    data stream.  As the specification says, they may appear at anytime.  There
    is not a requirement to send a clear code when the string table is full.

    It is the encoder's decision as to when the table should be cleared.  When
    the table is full, the encoder can chose to use the table as is, making no
    changes to it until the encoder chooses to clear it.  The encoder during
    this time sends out codes that are of the maximum Code Size.

    As we can see from the above, when the decoder's table is full, it must
    not change the table until a clear code is received.  The Code Size is that
    of the maximum Code Size.  Processing other than this is done normally.

    Because of a large base of decoders that do not handle the decompression in
    this manner, we ask developers of GIF encoding software to NOT implement
    this feature until at least January 1991 and later if they see that their
    particular market is not ready for it.  This will give developers of GIF
    decoding software time to implement this feature and to get it into the
    hands of their clients before the decoders start "breaking" on the new
    GIF's.  It is not required that encoders change their software to take
    advantage of the deferred clear code, but it is for decoders.

    APPLICATION EXTENSION BLOCK - APPLICATION IDENTIFIER

    There will be a Courtesy Directory file located on CompuServe in the PICS
    forum.  This directory will contain Application Identifiers for Application
    Extension Blocks that have been used by developers of GIF applications.
    This file is intended to help keep developers that wish to create
    Application Extension Blocks from using the same Application Identifiers.
    This is not an official directory; it is for voluntary participation only
    and does not guarantee that someone will not use the same identifier.

    E-Mail can be sent to Larry Wood (forum manager of PICS) indicating the
    request for inclusion in this file with an identifier.

                        GRAPHICS INTERCHANGE FORMAT(sm)

                                  Version 89a

                            (c)1987,1988,1989,1990

                                   Copyright
                            CompuServe Incorporated
                                Columbus, Ohio

CompuServe Incorporated                           Graphics Interchange Format
Document Date : 31 July 1990                            Programming Reference

                               Table of Contents

Disclaimer.................................................................  1

Foreword...................................................................  1

Licensing..................................................................  1

About the Document.........................................................  2

General Description........................................................  2

Version Numbers............................................................  2

The Encoder................................................................  3

The Decoder................................................................  3

Compliance.................................................................  3

About Recommendations......................................................  4

About Color Tables.........................................................  4

Blocks, Extensions and Scope...............................................  4

Block Sizes................................................................  5

Using GIF as an embedded protocol..........................................  5

Data Sub-blocks............................................................  5

Block Terminator...........................................................  6

Header.....................................................................  7

Logical Screen Descriptor..................................................  8

Global Color Table......................................................... 10

Image Descriptor........................................................... 11

Local Color Table.......................................................... 13

Table Based Image Data..................................................... 14

Graphic Control Extension.................................................. 15

Comment Extension.......................................................... 17

Plain Text Extension....................................................... 18

Application Extension...................................................... 21

Trailer.................................................................... 23

Quick Reference Table...................................................... 24

GIF Grammar................................................................ 25

Glossary................................................................... 27

Conventions................................................................ 28

Interlaced Images.......................................................... 29

Variable-Length-Code LZW Compression....................................... 30

On-line Capabilities Dialogue.............................................. 33
                                                                                1

1. Disclaimer.

The information provided herein is subject to change without notice. In no
event will CompuServe Incorporated be liable for damages, including any loss of
revenue, loss of profits or other incidental or consequential damages arising
out of the use or inability to use the information; CompuServe Incorporated
makes no claim as to the suitability of the information.

2. Foreword.

This document defines the Graphics Interchange Format(sm). The specification
given here defines version 89a, which is an extension of version 87a.

The Graphics Interchange Format(sm) as specified here should be considered
complete; any deviation from it should be considered invalid, including but not
limited to, the use of reserved or undefined fields within control or data
blocks, the inclusion of extraneous data within or between blocks, the use of
methods or algorithms not specifically listed as part of the format, etc. In
general, any and all deviations, extensions or modifications not specified in
this document should be considered to be in violation of the format and should
be avoided.

3. Licensing.

The Graphics Interchange Format(c) is the copyright property of CompuServe
Incorporated. Only CompuServe Incorporated is authorized to define, redefine,
enhance, alter, modify or change in any way the definition of the format.

CompuServe Incorporated hereby grants a limited, non-exclusive, royalty-free
license for the use of the Graphics Interchange Format(sm) in computer
software; computer software utilizing GIF(sm) must acknowledge ownership of the
Graphics Interchange Format and its Service Mark by CompuServe Incorporated, in
User and Technical Documentation. Computer software utilizing GIF, which is
distributed or may be distributed without User or Technical Documentation must
display to the screen or printer a message acknowledging ownership of the
Graphics Interchange Format and the Service Mark by CompuServe Incorporated; in
this case, the acknowledgement may be displayed in an opening screen or leading
banner, or a closing screen or trailing banner. A message such as the following
may be used:

      "The Graphics Interchange Format(c) is the Copyright property of
      CompuServe Incorporated. GIF(sm) is a Service Mark property of
      CompuServe Incorporated."

For further information, please contact :

      CompuServe Incorporated
      Graphics Technology Department
      5000 Arlington Center Boulevard
      Columbus, Ohio  43220
      U. S. A.

CompuServe Incorporated maintains a mailing list with all those individuals and
organizations who wish to receive copies of this document when it is corrected
                                                                         2
or revised. This service is offered free of charge; please provide us with your
mailing address.

4. About the Document.

This document describes in detail the definition of the Graphics Interchange
Format.  This document is intended as a programming reference; it is
recommended that the entire document be read carefully before programming,
because of the interdependence of the various parts. There is an individual
section for each of the Format blocks. Within each section, the sub-section
labeled Required Version refers to the version number that an encoder will have
to use if the corresponding block is used in the Data Stream. Within each
section, a diagram describes the individual fields in the block; the diagrams
are drawn vertically; top bytes in the diagram appear first in the Data Stream.
Bits within a byte are drawn most significant on the left end.  Multi-byte
numeric fields are ordered Least Significant Byte first. Numeric constants are
represented as Hexadecimal numbers, preceded by "0x".  Bit fields within a byte
are described in order from most significant bits to least significant bits.

5. General Description.

The Graphics Interchange Format(sm) defines a protocol intended for the on-line
transmission and interchange of raster graphic data in a way that is
independent of the hardware used in their creation or display.

The Graphics Interchange Format is defined in terms of blocks and sub-blocks
which contain relevant parameters and data used in the reproduction of a
graphic. A GIF Data Stream is a sequence of protocol blocks and sub-blocks
representing a collection of graphics. In general, the graphics in a Data
Stream are assumed to be related to some degree, and to share some control
information; it is recommended that encoders attempt to group together related
graphics in order to minimize hardware changes during processing and to
minimize control information overhead. For the same reason, unrelated graphics
or graphics which require resetting hardware parameters should be encoded
separately to the extent possible.

A Data Stream may originate locally, as when read from a file, or it may
originate remotely, as when transmitted over a data communications line. The
Format is defined with the assumption that an error-free Transport Level
Protocol is used for communications; the Format makes no provisions for
error-detection and error-correction.

The GIF Data Stream must be interpreted in context, that is, the application
program must rely on information external to the Data Stream to invoke the
decoder process.

6. Version Numbers.

The version number in the Header of a Data Stream is intended to identify the
minimum set of capabilities required of a decoder in order to fully process the
Data Stream.  An encoder should use the earliest possible version number that
includes all the blocks used in the Data Stream. Within each block section in
this document, there is an entry labeled Required Version which specifies the
                                                                                3
earliest version number that includes the corresponding block.  The encoder
should make every attempt to use the earliest version number covering all the
blocks in the Data Stream; the unnecessary use of later version numbers will
hinder processing by some decoders.

7. The Encoder.

The Encoder is the program used to create a GIF Data Stream. From raster data
and other information, the encoder produces the necessary control and data
blocks needed for reproducing the original graphics.

The encoder has the following primary responsibilities.

            - Include in the Data Stream all the necessary information to
            reproduce  the graphics.

            - Insure that a Data Stream is labeled with the earliest possible
            Version Number that will cover the definition of all the blocks in
            it; this is to ensure that the largest number of decoders can
            process the Data Stream.

            - Ensure encoding of the graphics in such a way that the decoding
            process is optimized. Avoid redundant information as much as
            possible.

            - To the extent possible, avoid grouping graphics which might
            require resetting hardware parameters during the decoding process.

            - Set to zero (off) each of the bits of each and every field
            designated as reserved. Note that some fields in the Logical Screen
            Descriptor and the Image Descriptor were reserved under Version
            87a, but are used under version 89a.

8. The Decoder.

The Decoder is the program used to process a GIF Data Stream. It processes the
Data Stream sequentially, parsing the various blocks and sub-blocks, using the
control information to set hardware and process parameters and interpreting the
data to render the graphics.

The decoder has the following primary responsibilities.

            - Process each graphic in the Data Stream in sequence, without
            delays other than those specified in the control information.

            - Set its hardware parameters to fit, as closely as possible, the
            control information contained in the Data Stream.

9. Compliance.

An encoder or a decoder is said to comply with a given version of the Graphics
Interchange Format if and only if it fully conforms with and correctly
implements the definition of the standard associated with that version.  An
                                                                         4

encoder or a decoder may be compliant with a given version number and not
compliant with some subsequent version.

10. About Recommendations.

Each block section in this document contains an entry labeled Recommendation;
this section lists a set of recommendations intended to guide and organize the
use of the particular blocks. Such recommendations are geared towards making
the functions of encoders and decoders more efficient, as well as making
optimal use of the communications bandwidth.  It is advised that these
recommendations be followed.

11. About Color Tables.

The GIF format utilizes color tables to render raster-based graphics. A color
table can have one of two different scopes: global or local. A Global Color
Table is used by all those graphics in the Data Stream which do not have a
Local Color Table associated with them. The scope of the Global Color Table is
the entire Data Stream. A Local Color Table is always associated with the
graphic that immediately follows it; the scope of a Local Color Table is
limited to that single graphic. A Local Color Table supersedes a Global Color
Table, that is, if a Data Stream contains a Global Color Table, and an image
has a Local Color Table associated with it, the decoder must save the Global
Color Table, use the Local Color Table to render the image, and then restore
the Global Color Table. Both types of color tables are optional, making it
possible for a Data Stream to contain numerous graphics without a color table
at all. For this reason, it is recommended that the decoder save the last
Global Color Table used until another Global Color Table is encountered. In
this way, a Data Stream which does not contain either a Global Color Table or
a Local Color Table may be processed using the last Global Color Table saved.
If a Global Color Table from a previous Stream is used, that table becomes the
Global Color Table of the present Stream. This is intended to reduce the
overhead incurred by color tables. In particular, it is recommended that an
encoder use only one Global Color Table if all the images in related Data
Streams can be rendered with the same table.  If no color table is available at
all, the decoder is free to use a system color table or a table of its own. In
that case, the decoder may use a color table with as many colors as its
hardware is able to support; it is recommended that such a table have black and
white as its first two entries, so that monochrome images can be rendered
adequately.

The Definition of the GIF Format allows for a Data Stream to contain only the
Header, the Logical Screen Descriptor, a Global Color Table and the GIF
Trailer. Such a Data Stream would be used to load a decoder with a Global Color
Table, in preparation for subsequent Data Streams without a color table at all.

12. Blocks, Extensions and Scope.

Blocks can be classified into three groups : Control, Graphic-Rendering and
Special Purpose.  Control blocks, such as the Header, the Logical Screen
Descriptor, the Graphic Control Extension and the Trailer, contain information
used to control the process of the Data Stream or information  used in setting
hardware parameters.  Graphic-Rendering blocks such as the Image Descriptor and
                                                                                5
the Plain Text Extension contain information and data used to render a graphic
on the display device. Special Purpose blocks such as the Comment Extension and
the Application Extension are neither used to control the process of the Data
Stream nor do they contain information or data used to render a graphic on the
display device. With the exception of the Logical Screen Descriptor and the
Global Color Table, whose scope is the entire Data Stream, all other Control
blocks have a limited scope, restricted to the Graphic-Rendering block that
follows them.  Special Purpose blocks do not delimit the scope of any Control
blocks; Special Purpose blocks are transparent to the decoding process.
Graphic-Rendering blocks and extensions are used as scope delimiters for
Control blocks and extensions. The labels used to identify labeled blocks fall
into three ranges : 0x00-0x7F (0-127) are the Graphic Rendering blocks,
excluding the Trailer (0x3B); 0x80-0xF9 (128-249) are the Control blocks;
0xFA-0xFF (250-255) are the Special Purpose blocks. These ranges are defined so
that decoders can handle block scope by appropriately identifying block labels,
even when the block itself cannot be processed.

13. Block Sizes.

The Block Size field in a block, counts the number of bytes remaining in the
block, not counting the Block Size field itself, and not counting the Block
Terminator, if one is to follow.  Blocks other than Data Blocks are intended to
be of fixed length; the Block Size field is provided in order to facilitate
skipping them, not to allow their size to change in the future.  Data blocks
and sub-blocks are of variable length to accommodate the amount of data.

14. Using GIF as an embedded protocol.

As an embedded protocol, GIF may be part of larger application protocols,
within which GIF is used to render graphics.  In such a case, the application
protocol could define a block within which the GIF Data Stream would be
contained. The application program would then invoke a GIF decoder upon
encountering a block of type GIF.  This approach is recommended in favor of
using Application Extensions, which become overhead for all other applications
that do not process them. Because a GIF Data Stream must be processed in
context, the application must rely on some means of identifying the GIF Data
Stream outside of the Stream itself.

15. Data Sub-blocks.

      a. Description. Data Sub-blocks are units containing data. They do not
      have a label, these blocks are processed in the context of control
      blocks, wherever data blocks are specified in the format. The first byte
      of the Data sub-block indicates the number of data bytes to follow. A
      data sub-block may contain from 0 to 255 data bytes. The size of the
      block does not account for the size byte itself, therefore, the empty
      sub-block is one whose size field contains 0x00.

      b. Required Version.  87a.
                                                                         6
      c. Syntax.

      7 6 5 4 3 2 1 0        Field Name                    Type
     +---------------+
  0  |               |       Block Size                    Byte
     +---------------+
  1  |               |
     +-             -+
  2  |               |
     +-             -+
  3  |               |
     +-             -+
     |               |       Data Values                   Byte
     +-             -+
 up  |               |
     +-   . . . .   -+
 to  |               |
     +-             -+
     |               |
     +-             -+
255  |               |
     +---------------+

            i) Block Size - Number of bytes in the Data Sub-block; the size
            must be within 0 and 255 bytes, inclusive.

            ii) Data Values - Any 8-bit value. There must be exactly as many
            Data Values as specified by the Block Size field.

      d. Extensions and Scope. This type of block always occurs as part of a
      larger unit. It does not have a scope of itself.

      e. Recommendation. None.

16. Block Terminator.

      a. Description. This zero-length Data Sub-block is used to terminate a
      sequence of Data Sub-blocks. It contains a single byte in the position of
      the Block Size field and does not contain data.

      b. Required Version.  87a.

      c. Syntax.

      7 6 5 4 3 2 1 0        Field Name                    Type
     +---------------+
  0  |               |       Block Size                    Byte
     +---------------+

            i) Block Size - Number of bytes in the Data Sub-block; this field
            contains the fixed value 0x00.

            ii) Data Values - This block does not contain any data.
                                                                                7
      d. Extensions and Scope. This block terminates the immediately preceding
      sequence of Data Sub-blocks. This block cannot be modified by any
      extension.

      e. Recommendation. None.

17. Header.

      a. Description. The Header identifies the GIF Data Stream in context. The
      Signature field marks the beginning of the Data Stream, and the Version
      field identifies the set of capabilities required of a decoder to fully
      process the Data Stream.  This block is REQUIRED; exactly one Header must
      be present per Data Stream.

      b. Required Version.  Not applicable. This block is not subject to a
      version number. This block must appear at the beginning of every Data
      Stream.

      c. Syntax.

      7 6 5 4 3 2 1 0        Field Name                    Type
     +---------------+
   0 |               |       Signature                     3 Bytes
     +-             -+
   1 |               |
     +-             -+
   2 |               |
     +---------------+
   3 |               |       Version                       3 Bytes
     +-             -+
   4 |               |
     +-             -+
   5 |               |
     +---------------+

            i) Signature - Identifies the GIF Data Stream. This field contains
            the fixed value 'GIF'.

            ii) Version - Version number used to format the data stream.
            Identifies the minimum set of capabilities necessary to a decoder
            to fully process the contents of the Data Stream.

            Version Numbers as of 10 July 1990 :       "87a" - May 1987
                                                       "89a" - July 1989

            Version numbers are ordered numerically increasing on the first two
            digits starting with 87 (87,88,...,99,00,...,85,86) and
            alphabetically increasing on the third character (a,...,z).

            iii) Extensions and Scope. The scope of this block is the entire
            Data Stream. This block cannot be modified by any extension.
                                                                         8
        d. Recommendations.

            i) Signature - This field identifies the beginning of the GIF Data
            Stream; it is not intended to provide a unique signature for the
            identification of the data. It is recommended that the GIF Data
            Stream be identified externally by the application. (Refer to
            Appendix G for on-line identification of the GIF Data Stream.)

            ii) Version - ENCODER : An encoder should use the earliest possible
            version number that defines all the blocks used in the Data Stream.
            When two or more Data Streams are combined, the latest of the
            individual version numbers should be used for the resulting Data
            Stream. DECODER : A decoder should attempt to process the data
            stream to the best of its ability; if it encounters a version
            number which it is not capable of processing fully, it should
            nevertheless, attempt to process the data stream to the best of its
            ability, perhaps after warning the user that the data may be
            incomplete.

18. Logical Screen Descriptor.

      a. Description.  The Logical Screen Descriptor contains the parameters
      necessary to define the area of the display device within which the
      images will be rendered.  The coordinates in this block are given with
      respect to the top-left corner of the virtual screen; they do not
      necessarily refer to absolute coordinates on the display device.  This
      implies that they could refer to window coordinates in a window-based
      environment or printer coordinates when a printer is used.

      This block is REQUIRED; exactly one Logical Screen Descriptor must be
      present per Data Stream.

      b. Required Version.  Not applicable. This block is not subject to a
      version number. This block must appear immediately after the Header.

      c. Syntax.

      7 6 5 4 3 2 1 0        Field Name                    Type
     +---------------+
  0  |               |       Logical Screen Width          Unsigned
     +-             -+
  1  |               |
     +---------------+
  2  |               |       Logical Screen Height         Unsigned
     +-             -+
  3  |               |
     +---------------+
  4  | |     | |     |                      See below
     +---------------+
  5  |               |       Background Color Index        Byte
     +---------------+
  6  |               |       Pixel Aspect Ratio            Byte
     +---------------+

                                                                         9

       =      Global Color Table Flag       1 Bit
                             Color Resolution              3 Bits
                             Sort Flag                     1 Bit
                             Size of Global Color Table    3 Bits

            i) Logical Screen Width - Width, in pixels, of the Logical Screen
            where the images will be rendered in the displaying device.

            ii) Logical Screen Height - Height, in pixels, of the Logical
            Screen where the images will be rendered in the displaying device.

            iii) Global Color Table Flag - Flag indicating the presence of a
            Global Color Table; if the flag is set, the Global Color Table will
            immediately follow the Logical Screen Descriptor. This flag also
            selects the interpretation of the Background Color Index; if the
            flag is set, the value of the Background Color Index field should
            be used as the table index of the background color. (This field is
            the most significant bit of the byte.)

            Values :    0 -   No Global Color Table follows, the Background
                              Color Index field is meaningless.
                        1 -   A Global Color Table will immediately follow, the
                              Background Color Index field is meaningful.

            iv) Color Resolution - Number of bits per primary color available
            to the original image, minus 1. This value represents the size of
            the entire palette from which the colors in the graphic were
            selected, not the number of colors actually used in the graphic.
            For example, if the value in this field is 3, then the palette of
            the original image had 4 bits per primary color available to create
            the image.  This value should be set to indicate the richness of
            the original palette, even if not every color from the whole
            palette is available on the source machine.

            v) Sort Flag - Indicates whether the Global Color Table is sorted.
            If the flag is set, the Global Color Table is sorted, in order of
            decreasing importance. Typically, the order would be decreasing
            frequency, with most frequent color first. This assists a decoder,
            with fewer available colors, in choosing the best subset of colors;
            the decoder may use an initial segment of the table to render the
            graphic.

            Values :    0 -   Not ordered.
                        1 -   Ordered by decreasing importance, most
                              important color first.

            vi) Size of Global Color Table - If the Global Color Table Flag is
            set to 1, the value in this field is used to calculate the number
            of bytes contained in the Global Color Table. To determine that
            actual size of the color table, raise 2 to [the value of the field
            + 1].  Even if there is no Global Color Table specified, set this
            field according to the above formula so that decoders can choose
            the best graphics mode to display the stream in.  (This field is
            made up of the 3 least significant bits of the byte.)

            vii) Background Color Index - Index into the Global Color Table for

                                                                        10

            the Background Color. The Background Color is the color used for
            those pixels on the screen that are not covered by an image. If the
            Global Color Table Flag is set to (zero), this field should be zero
            and should be ignored.

            viii) Pixel Aspect Ratio - Factor used to compute an approximation
            of the aspect ratio of the pixel in the original image.  If the
            value of the field is not 0, this approximation of the aspect ratio
            is computed based on the formula:

            Aspect Ratio = (Pixel Aspect Ratio + 15) / 64

            The Pixel Aspect Ratio is defined to be the quotient of the pixel's
            width over its height.  The value range in this field allows
            specification of the widest pixel of 4:1 to the tallest pixel of
            1:4 in increments of 1/64th.

            Values :        0 -   No aspect ratio information is given.
                       1..255 -   Value used in the computation.

      d. Extensions and Scope. The scope of this block is the entire Data
      Stream. This block cannot be modified by any extension.

      e. Recommendations. None.

19. Global Color Table.

      a. Description. This block contains a color table, which is a sequence of
      bytes representing red-green-blue color triplets. The Global Color Table
      is used by images without a Local Color Table and by Plain Text
      Extensions. Its presence is marked by the Global Color Table Flag being
      set to 1 in the Logical Screen Descriptor; if present, it immediately
      follows the Logical Screen Descriptor and contains a number of bytes
      equal to
                    3 x 2^(Size of Global Color Table+1).

      This block is OPTIONAL; at most one Global Color Table may be present
      per Data Stream.

      b. Required Version.  87a

                                                                        11

      c. Syntax.

      7 6 5 4 3 2 1 0        Field Name                    Type
     +===============+
  0  |               |       Red 0                         Byte
     +-             -+
  1  |               |       Green 0                       Byte
     +-             -+
  2  |               |       Blue 0                        Byte
     +-             -+
  3  |               |       Red 1                         Byte
     +-             -+
     |               |       Green 1                       Byte
     +-             -+
 up  |               |
     +-   . . . .   -+       ...
 to  |               |
     +-             -+
     |               |       Green 255                     Byte
     +-             -+
767  |               |       Blue 255                      Byte
     +===============+

      d. Extensions and Scope. The scope of this block is the entire Data
      Stream. This block cannot be modified by any extension.

      e. Recommendation. None.

20. Image Descriptor.

      a. Description. Each image in the Data Stream is composed of an Image
      Descriptor, an optional Local Color Table, and the image data.  Each
      image must fit within the boundaries of the Logical Screen, as defined
      in the Logical Screen Descriptor.

      The Image Descriptor contains the parameters necessary to process a table
      based image. The coordinates given in this block refer to coordinates
      within the Logical Screen, and are given in pixels. This block is a
      Graphic-Rendering Block, optionally preceded by one or more Control
      blocks such as the Graphic Control Extension, and may be optionally
      followed by a Local Color Table; the Image Descriptor is always followed
      by the image data.

      This block is REQUIRED for an image.  Exactly one Image Descriptor must
      be present per image in the Data Stream.  An unlimited number of images
      may be present per Data Stream.

      b. Required Version.  87a.

                                                                        12

      c. Syntax.

      7 6 5 4 3 2 1 0        Field Name                    Type
     +---------------+
  0  |               |       Image Separator               Byte
     +---------------+
  1  |               |       Image Left Position           Unsigned
     +-             -+
  2  |               |
     +---------------+
  3  |               |       Image Top Position            Unsigned
     +-             -+
  4  |               |
     +---------------+
  5  |               |       Image Width                   Unsigned
     +-             -+
  6  |               |
     +---------------+
  7  |               |       Image Height                  Unsigned
     +-             -+
  8  |               |
     +---------------+
  9  | | | |   |     |                      See below
     +---------------+

       =      Local Color Table Flag        1 Bit
                             Interlace Flag                1 Bit
                             Sort Flag                     1 Bit
                             Reserved                      2 Bits
                             Size of Local Color Table     3 Bits

           i) Image Separator - Identifies the beginning of an Image
           Descriptor. This field contains the fixed value 0x2C.

           ii) Image Left Position - Column number, in pixels, of the left edge
           of the image, with respect to the left edge of the Logical Screen.
           Leftmost column of the Logical Screen is 0.

           iii) Image Top Position - Row number, in pixels, of the top edge of
           the image with respect to the top edge of the Logical Screen. Top
           row of the Logical Screen is 0.

           iv) Image Width - Width of the image in pixels.

           v) Image Height - Height of the image in pixels.

           vi) Local Color Table Flag - Indicates the presence of a Local Color
           Table immediately following this Image Descriptor. (This field is
           the most significant bit of the byte.)

           Values :    0 -   Local Color Table is not present. Use
                             Global Color Table if available.
                       1 -   Local Color Table present, and to follow
                             immediately after this Image Descriptor.

                                                                        13

           vii) Interlace Flag - Indicates if the image is interlaced. An image
           is interlaced in a four-pass interlace pattern; see Appendix E for
           details.

           Values :    0 - Image is not interlaced.
                       1 - Image is interlaced.

            viii) Sort Flag - Indicates whether the Local Color Table is
            sorted.  If the flag is set, the Local Color Table is sorted, in
            order of decreasing importance. Typically, the order would be
            decreasing frequency, with most frequent color first. This assists
            a decoder, with fewer available colors, in choosing the best subset
            of colors; the decoder may use an initial segment of the table to
            render the graphic.

            Values :    0 -   Not ordered.
                        1 -   Ordered by decreasing importance, most
                              important color first.

            ix) Size of Local Color Table - If the Local Color Table Flag is
            set to 1, the value in this field is used to calculate the number
            of bytes contained in the Local Color Table. To determine that
            actual size of the color table, raise 2 to the value of the field
            + 1. This value should be 0 if there is no Local Color Table
            specified. (This field is made up of the 3 least significant bits
            of the byte.)

     d. Extensions and Scope. The scope of this block is the Table-based Image
     Data Block that follows it. This block may be modified by the Graphic
     Control Extension.

     e. Recommendation. None.

21. Local Color Table.

     a. Description. This block contains a color table, which is a sequence of
     bytes representing red-green-blue color triplets. The Local Color Table
     is used by the image that immediately follows. Its presence is marked by
     the Local Color Table Flag being set to 1 in the Image Descriptor; if
     present, the Local Color Table immediately follows the Image Descriptor
     and contains a number of bytes equal to
                          3x2^(Size of Local Color Table+1).
     If present, this color table temporarily becomes the active color table
     and the following image should be processed using it. This block is
     OPTIONAL; at most one Local Color Table may be present per Image
     Descriptor and its scope is the single image associated with the Image
     Descriptor that precedes it.

     b. Required Version.  87a.

                                                                        14

     c. Syntax.

      7 6 5 4 3 2 1 0        Field Name                    Type
     +===============+
  0  |               |       Red 0                         Byte
     +-             -+
  1  |               |       Green 0                       Byte
     +-             -+
  2  |               |       Blue 0                        Byte
     +-             -+
  3  |               |       Red 1                         Byte
     +-             -+
     |               |       Green 1                       Byte
     +-             -+
 up  |               |
     +-   . . . .   -+       ...
 to  |               |
     +-             -+
     |               |       Green 255                     Byte
     +-             -+
767  |               |       Blue 255                      Byte
     +===============+

     d. Extensions and Scope. The scope of this block is the Table-based Image
     Data Block that immediately follows it. This block cannot be modified by
     any extension.

     e. Recommendations. None.

22. Table Based Image Data.

     a. Description. The image data for a table based image consists of a
     sequence of sub-blocks, of size at most 255 bytes each, containing an
     index into the active color table, for each pixel in the image.  Pixel
     indices are in order of left to right and from top to bottom.  Each index
     must be within the range of the size of the active color table, starting
     at 0. The sequence of indices is encoded using the LZW Algorithm with
     variable-length code, as described in Appendix F

     b. Required Version.  87a.

     c. Syntax. The image data format is as follows:

      7 6 5 4 3 2 1 0        Field Name                    Type
     +---------------+
     |               |       LZW Minimum Code Size         Byte
     +---------------+

     +===============+
     |               |
     /               /       Image Data                    Data Sub-blocks
     |               |
     +===============+

                                                                        15

            i) LZW Minimum Code Size.  This byte determines the initial number
            of bits used for LZW codes in the image data, as described in
            Appendix F.

     d. Extensions and Scope. This block has no scope, it contains raster
     data. Extensions intended to modify a Table-based image must appear
     before the corresponding Image Descriptor.

     e. Recommendations. None.

23. Graphic Control Extension.

      a. Description. The Graphic Control Extension contains parameters used
      when processing a graphic rendering block. The scope of this extension is
      the first graphic rendering block to follow. The extension contains only
      one data sub-block.

      This block is OPTIONAL; at most one Graphic Control Extension may precede
      a graphic rendering block. This is the only limit to the number of
      Graphic Control Extensions that may be contained in a Data Stream.

      b. Required Version.  89a.

      c. Syntax.

      7 6 5 4 3 2 1 0        Field Name                    Type
     +---------------+
  0  |               |       Extension Introducer          Byte
     +---------------+
  1  |               |       Graphic Control Label         Byte
     +---------------+

     +---------------+
  0  |               |       Block Size                    Byte
     +---------------+
  1  |     |     | | |                      See below
     +---------------+
  2  |               |       Delay Time                    Unsigned
     +-             -+
  3  |               |
     +---------------+
  4  |               |       Transparent Color Index       Byte
     +---------------+

     +---------------+
  0  |               |       Block Terminator              Byte
     +---------------+

        =     Reserved                      3 Bits
                             Disposal Method               3 Bits
                             User Input Flag               1 Bit
                             Transparent Color Flag        1 Bit

            i) Extension Introducer - Identifies the beginning of an extension

                                                                        16

            block. This field contains the fixed value 0x21.

            ii) Graphic Control Label - Identifies the current block as a
            Graphic Control Extension. This field contains the fixed value
            0xF9.

            iii) Block Size - Number of bytes in the block, after the Block
            Size field and up to but not including the Block Terminator.  This
            field contains the fixed value 4.

            iv) Disposal Method - Indicates the way in which the graphic is to
            be treated after being displayed.

            Values :    0 -   No disposal specified. The decoder is
                              not required to take any action.
                        1 -   Do not dispose. The graphic is to be left
                              in place.
                        2 -   Restore to background color. The area used by the
                              graphic must be restored to the background color.
                        3 -   Restore to previous. The decoder is required to
                              restore the area overwritten by the graphic with
                              what was there prior to rendering the graphic.
                     4-7 -    To be defined.

            v) User Input Flag - Indicates whether or not user input is
            expected before continuing. If the flag is set, processing will
            continue when user input is entered. The nature of the User input
            is determined by the application (Carriage Return, Mouse Button
            Click, etc.).

            Values :    0 -   User input is not expected.
                        1 -   User input is expected.

            When a Delay Time is used and the User Input Flag is set,
            processing will continue when user input is received or when the
            delay time expires, whichever occurs first.

            vi) Transparency Flag - Indicates whether a transparency index is
            given in the Transparent Index field. (This field is the least
            significant bit of the byte.)

            Values :    0 -   Transparent Index is not given.
                        1 -   Transparent Index is given.

            vii) Delay Time - If not 0, this field specifies the number of
            hundredths (1/100) of a second to wait before continuing with the
            processing of the Data Stream. The clock starts ticking immediately
            after the graphic is rendered. This field may be used in
            conjunction with the User Input Flag field.

            viii) Transparency Index - The Transparency Index is such that when
            encountered, the corresponding pixel of the display device is not
            modified and processing goes on to the next pixel. The index is
            present if and only if the Transparency Flag is set to 1.

            ix) Block Terminator - This zero-length data block marks the end of

                                                                        17

            the Graphic Control Extension.

      d. Extensions and Scope. The scope of this Extension is the graphic
      rendering block that follows it; it is possible for other extensions to
      be present between this block and its target. This block can modify the
      Image Descriptor Block and the Plain Text Extension.

      e. Recommendations.

            i) Disposal Method - The mode Restore To Previous is intended to be
            used in small sections of the graphic; the use of this mode imposes
            severe demands on the decoder to store the section of the graphic
            that needs to be saved. For this reason, this mode should be used
            sparingly.  This mode is not intended to save an entire graphic or
            large areas of a graphic; when this is the case, the encoder should
            make every attempt to make the sections of the graphic to be
            restored be separate graphics in the data stream. In the case where
            a decoder is not capable of saving an area of a graphic marked as
            Restore To Previous, it is recommended that a decoder restore to
            the background color.

            ii) User Input Flag - When the flag is set, indicating that user
            input is expected, the decoder may sound the bell (0x07) to alert
            the user that input is being expected.  In the absence of a
            specified Delay Time, the decoder should wait for user input
            indefinitely.  It is recommended that the encoder not set the User
            Input Flag without a Delay Time specified.

24. Comment Extension.

      a. Description. The Comment Extension contains textual information which
      is not part of the actual graphics in the GIF Data Stream. It is suitable
      for including comments about the graphics, credits, descriptions or any
      other type of non-control and non-graphic data.  The Comment Extension
      may be ignored by the decoder, or it may be saved for later processing;
      under no circumstances should a Comment Extension disrupt or interfere
      with the processing of the Data Stream.

      This block is OPTIONAL; any number of them may appear in the Data Stream.

      b. Required Version.  89a.

                                                                        18

      c. Syntax.

      7 6 5 4 3 2 1 0        Field Name                    Type
     +---------------+
  0  |               |       Extension Introducer          Byte
     +---------------+
  1  |               |       Comment Label                 Byte
     +---------------+

     +===============+
     |               |
  N  |               |       Comment Data                  Data Sub-blocks
     |               |
     +===============+

     +---------------+
  0  |               |       Block Terminator              Byte
     +---------------+

            i) Extension Introducer - Identifies the beginning of an extension
            block. This field contains the fixed value 0x21.

            ii) Comment Label - Identifies the block as a Comment Extension.
            This field contains the fixed value 0xFE.

            iii) Comment Data - Sequence of sub-blocks, each of size at most
            255 bytes and at least 1 byte, with the size in a byte preceding
            the data.  The end of the sequence is marked by the Block
            Terminator.

            iv) Block Terminator - This zero-length data block marks the end of
            the Comment Extension.

      d. Extensions and Scope. This block does not have scope. This block
      cannot be modified by any extension.

      e. Recommendations.

            i) Data - This block is intended for humans.  It should contain
            text using the 7-bit ASCII character set. This block should
            not be used to store control information for custom processing.

            ii) Position - This block may appear at any point in the Data
            Stream at which a block can begin; however, it is recommended that
            Comment Extensions do not interfere with Control or Data blocks;
            they should be located at the beginning or at the end of the Data
            Stream to the extent possible.

25. Plain Text Extension.

      a. Description. The Plain Text Extension contains textual data and the
      parameters necessary to render that data as a graphic, in a simple form.
      The textual data will be encoded with the 7-bit printable ASCII
      characters.  Text data are rendered using a grid of character cells

                                                                        19

      defined by the parameters in the block fields. Each character is rendered
      in an individual cell. The textual data in this block is to be rendered
      as mono-spaced characters, one character per cell, with a best fitting
      font and size. For further information, see the section on
      Recommendations below. The data characters are taken sequentially from
      the data portion of the block and rendered within a cell, starting with
      the upper left cell in the grid and proceeding from left to right and
      from top to bottom. Text data is rendered until the end of data is
      reached or the character grid is filled.  The Character Grid contains an
      integral number of cells; in the case that the cell dimensions do not
      allow for an integral number, fractional cells must be discarded; an
      encoder must be careful to specify the grid dimensions accurately so that
      this does not happen. This block requires a Global Color Table to be
      available; the colors used by this block reference the Global Color Table
      in the Stream if there is one, or the Global Color Table from a previous
      Stream, if one was saved. This block is a graphic rendering block,
      therefore it may be modified by a Graphic Control Extension.  This block
      is OPTIONAL; any number of them may appear in the Data Stream.

      b. Required Version.  89a.

                                                                        20

      c. Syntax.

      7 6 5 4 3 2 1 0        Field Name                    Type
     +---------------+
  0  |               |       Extension Introducer          Byte
     +---------------+
  1  |               |       Plain Text Label              Byte
     +---------------+

     +---------------+
  0  |               |       Block Size                    Byte
     +---------------+
  1  |               |       Text Grid Left Position       Unsigned
     +-             -+
  2  |               |
     +---------------+
  3  |               |       Text Grid Top Position        Unsigned
     +-             -+
  4  |               |
     +---------------+
  5  |               |       Text Grid Width               Unsigned
     +-             -+
  6  |               |
     +---------------+
  7  |               |       Text Grid Height              Unsigned
     +-             -+
  8  |               |
     +---------------+
  9  |               |       Character Cell Width          Byte
     +---------------+
 10  |               |       Character Cell Height         Byte
     +---------------+
 11  |               |       Text Foreground Color Index   Byte
     +---------------+
 12  |               |       Text Background Color Index   Byte
     +---------------+

     +===============+
     |               |
  N  |               |       Plain Text Data               Data Sub-blocks
     |               |
     +===============+

     +---------------+
  0  |               |       Block Terminator              Byte
     +---------------+

            i) Extension Introducer - Identifies the beginning of an extension
            block. This field contains the fixed value 0x21.

            ii) Plain Text Label - Identifies the current block as a Plain Text
            Extension. This field contains the fixed value 0x01.

            iii) Block Size - Number of bytes in the extension, after the Block
            Size field and up to but not including the beginning of the data
            portion. This field contains the fixed value 12.

                                                                        21

            iv) Text Grid Left Position - Column number, in pixels, of the left
            edge of the text grid, with respect to the left edge of the Logical
            Screen.

            v) Text Grid Top Position - Row number, in pixels, of the top edge
            of the text grid, with respect to the top edge of the Logical
            Screen.

            vi) Image Grid Width - Width of the text grid in pixels.

            vii) Image Grid Height - Height of the text grid in pixels.

            viii) Character Cell Width - Width, in pixels, of each cell in the
            grid.

            ix) Character Cell Height - Height, in pixels, of each cell in the
            grid.

            x) Text Foreground Color Index - Index into the Global Color Table
            to be used to render the text foreground.

            xi) Text Background Color Index - Index into the Global Color Table
            to be used to render the text background.

            xii) Plain Text Data - Sequence of sub-blocks, each of size at most
            255 bytes and at least 1 byte, with the size in a byte preceding
            the data.  The end of the sequence is marked by the Block
            Terminator.

            xiii) Block Terminator - This zero-length data block marks the end
            of the Plain Text Data Blocks.

      d. Extensions and Scope. The scope of this block is the Plain Text Data
      Block contained in it. This block may be modified by the Graphic Control
      Extension.

      e. Recommendations. The data in the Plain Text Extension is assumed to be
      preformatted. The selection of font and size is left to the discretion of
      the decoder.  If characters less than 0x20 or greater than 0xf7 are
      encountered, it is recommended that the decoder display a Space character
      (0x20). The encoder should use grid and cell dimensions such that an
      integral number of cells fit in the grid both horizontally as well as
      vertically.  For broadest compatibility, character cell dimensions should
      be around 8x8 or 8x16 (width x height); consider an image for unusual
      sized text.

26. Application Extension.

      a. Description. The Application Extension contains application-specific
      information; it conforms with the extension block syntax, as described
      below, and its block label is 0xFF.

      b. Required Version.  89a.

                                                                        22

      c. Syntax.

      7 6 5 4 3 2 1 0        Field Name                    Type
     +---------------+
  0  |               |       Extension Introducer          Byte
     +---------------+
  1  |               |       Extension Label               Byte
     +---------------+

     +---------------+
  0  |               |       Block Size                    Byte
     +---------------+
  1  |               |
     +-             -+
  2  |               |
     +-             -+
  3  |               |       Application Identifier        8 Bytes
     +-             -+
  4  |               |
     +-             -+
  5  |               |
     +-             -+
  6  |               |
     +-             -+
  7  |               |
     +-             -+
  8  |               |
     +---------------+
  9  |               |
     +-             -+
 10  |               |       Appl. Authentication Code     3 Bytes
     +-             -+
 11  |               |
     +---------------+

     +===============+
     |               |
     |               |       Application Data              Data Sub-blocks
     |               |
     |               |
     +===============+

     +---------------+
  0  |               |       Block Terminator              Byte
     +---------------+

            i) Extension Introducer - Defines this block as an extension. This
            field contains the fixed value 0x21.

            ii) Application Extension Label - Identifies the block as an
            Application Extension. This field contains the fixed value 0xFF.

            iii) Block Size - Number of bytes in this extension block,
            following the Block Size field, up to but not including the
            beginning of the Application Data. This field contains the fixed
            value 11.

                                                                        23

            iv) Application Identifier - Sequence of eight printable ASCII
            characters used to identify the application owning the Application
            Extension.

            v) Application Authentication Code - Sequence of three bytes used
            to authenticate the Application Identifier. An Application program
            may use an algorithm to compute a binary code that uniquely
            identifies it as the application owning the Application Extension.

      d. Extensions and Scope. This block does not have scope. This block
      cannot be modified by any extension.

      e. Recommendation. None.

27. Trailer.

      a. Description. This block is a single-field block indicating the end of
      the GIF Data Stream.  It contains the fixed value 0x3B.

      b. Required Version.  87a.

      c. Syntax.

      7 6 5 4 3 2 1 0        Field Name                    Type
     +---------------+
  0  |               |       GIF Trailer                   Byte
     +---------------+

      d. Extensions and Scope. This block does not have scope, it terminates
      the GIF Data Stream. This block may not be modified by any extension.

      e. Recommendations. None.

                                                                        24

Appendix
A. Quick Reference Table.

Block Name                  Required   Label       Ext.   Vers.
Application Extension       Opt. (*)   0xFF (255)  yes    89a
Comment Extension           Opt. (*)   0xFE (254)  yes    89a
Global Color Table          Opt. (1)   none        no     87a
Graphic Control Extension   Opt. (*)   0xF9 (249)  yes    89a
Header                      Req. (1)   none        no     N/A
Image Descriptor            Opt. (*)   0x2C (044)  no     87a (89a)
Local Color Table           Opt. (*)   none        no     87a
Logical Screen Descriptor   Req. (1)   none        no     87a (89a)
Plain Text Extension        Opt. (*)   0x01 (001)  yes    89a
Trailer                     Req. (1)   0x3B (059)  no     87a

Unlabeled Blocks
Header                      Req. (1)   none        no     N/A
Logical Screen Descriptor   Req. (1)   none        no     87a (89a)
Global Color Table          Opt. (1)   none        no     87a
Local Color Table           Opt. (*)   none        no     87a

Graphic-Rendering Blocks
Plain Text Extension        Opt. (*)   0x01 (001)  yes    89a
Image Descriptor            Opt. (*)   0x2C (044)  no     87a (89a)

Control Blocks
Graphic Control Extension   Opt. (*)   0xF9 (249)  yes    89a

Special Purpose Blocks
Trailer                     Req. (1)   0x3B (059)  no     87a
Comment Extension           Opt. (*)   0xFE (254)  yes    89a
Application Extension       Opt. (*)   0xFF (255)  yes    89a

legend:           (1)   if present, at most one occurrence
                  (*)   zero or more occurrences
                  (+)   one or more occurrences

Notes : The Header is not subject to Version Numbers.
(89a) The Logical Screen Descriptor and the Image Descriptor retained their
syntax from version 87a to version 89a, but some fields reserved under version
87a are used under version 89a.

                                                                        25

Appendix
B. GIF Grammar.

A Grammar is a form of notation to represent the sequence in which certain
objects form larger objects.  A grammar is also used to represent the number of
objects that can occur at a given position.  The grammar given here represents
the sequence of blocks that form the GIF Data Stream. A grammar is given by
listing its rules.  Each rule consists of the left-hand side, followed by some
form of equals sign, followed by the right-hand side.  In a rule, the
right-hand side describes how the left-hand side is defined. The right-hand
side consists of a sequence of entities, with the possible presence of special
symbols. The following legend defines the symbols used in this grammar for GIF.

Legend:           <>    grammar word
                  ::=   defines symbol
                  *     zero or more occurrences
                  +     one or more occurrences
                  |     alternate element
                  []    optional element

Example:

 ::= Header  * Trailer

This rule defines the entity  as follows. It must begin with a
Header. The Header is followed by an entity called Logical Screen, which is
defined below by another rule. The Logical Screen is followed by the entity
Data, which is also defined below by another rule. Finally, the entity Data is
followed by the Trailer.  Since there is no rule defining the Header or the
Trailer, this means that these blocks are defined in the document.  The entity
Data has a special symbol (*) following it which means that, at this position,
the entity Data may be repeated any number of times, including 0 times. For
further reading on this subject, refer to a standard text on Programming
Languages.

The Grammar.

 ::=     Header  * Trailer

 ::=      Logical Screen Descriptor [Global Color Table]

 ::=                  |

 ::=       [Graphic Control Extension]

 ::=    |
                               Plain Text Extension

 ::=   Image Descriptor [Local Color Table] Image Data

 ::=    Application Extension  |
                               Comment Extension

                                                                        26

NOTE : The grammar indicates that it is possible for a GIF Data Stream to
contain the Header, the Logical Screen Descriptor, a Global Color Table and the
GIF Trailer. This special case is used to load a GIF decoder with a Global
Color Table, in preparation for subsequent Data Streams without color tables at
all.

                                                                        27

Appendix
C. Glossary.

Active Color Table - Color table used to render the next graphic. If the next
graphic is an image which has a Local Color Table associated with it, the
active color table becomes the Local Color Table associated with that image.
If the next graphic is an image without a Local Color Table, or a Plain Text
Extension, the active color table is the Global Color Table associated with the
Data Stream, if there is one; if there is no Global Color Table in the Data
Stream, the active color table is a color table saved from a previous Data
Stream, or one supplied by the decoder.

Block - Collection of bytes forming a protocol unit. In general, the term
includes labeled and unlabeled blocks, as well as Extensions.

Data Stream - The GIF Data Stream is composed of blocks and sub-blocks
representing images and graphics, together with control information to render
them on a display device. All control and data blocks in the Data Stream must
follow the Header and must precede the Trailer.

Decoder - A program capable of processing a GIF Data Stream to render the
images and graphics contained in it.

Encoder - A program capable of capturing and formatting image and graphic
raster data, following the definitions of the Graphics Interchange Format.

Extension - A protocol block labeled by the Extension Introducer 0x21.

Extension Introducer - Label (0x21) defining an Extension.

Graphic - Data which can be rendered on the screen by virtue of some algorithm.
The term graphic is more general than the term image; in addition to images,
the term graphic also includes data such as text, which is rendered using
character bit-maps.

Image - Data representing a picture or a drawing; an image is represented by an
array of pixels called the raster of the image.

Raster - Array of pixel values representing an image.

                                                                        28

Appendix
D. Conventions.

Animation - The Graphics Interchange Format is not intended as a platform for
animation, even though it can be done in a limited way.

Byte Ordering - Unless otherwise stated, multi-byte numeric fields are ordered
with the Least Significant Byte first.

Color Indices - Color indices always refer to the active color table, either
the Global Color Table or the Local Color Table.

Color Order - Unless otherwise stated, all triple-component RGB color values
are specified in Red-Green-Blue order.

Color Tables - Both color tables, the Global and the Local, are optional; if
present, the Global Color Table is to be used with every image in the Data
Stream for which a Local Color Table is not given; if present, a Local Color
Table overrides the Global Color Table.  However, if neither color table is
present, the application program is free to use an arbitrary color table. If
the graphics in several Data Streams are related and all use the same color
table, an encoder could place the color table as the Global Color Table in the
first Data Stream and leave subsequent Data Streams without a Global Color
Table or any Local Color Tables; in this way, the overhead for the table is
eliminated.  It is recommended that the decoder save the previous Global Color
Table to be used with the Data Stream that follows, in case it does not contain
either a Global Color Table or any Local Color Tables. In general, this allows
the application program to use past color tables, significantly reducing
transmission overhead.

Extension Blocks - Extensions are defined using the Extension Introducer code
to mark the beginning of the block, followed by a block label, identifying the
type of extension.  Extension Codes are numbers in the range from 0x00 to 0xFF,
inclusive. Special purpose extensions are transparent to the decoder and may be
omitted when transmitting the Data Stream on-line. The GIF capabilities
dialogue makes the provision for the receiver to request the transmission of
all blocks; the default state in this regard is no transmission of Special
purpose blocks.

Reserved Fields - All Reserved Fields are expected to have each bit set to zero
(off).

                                                                        29

Appendix
E. Interlaced Images.

The rows of an Interlaced images are arranged in the following order:

      Group 1 : Every 8th. row, starting with row 0.              (Pass 1)
      Group 2 : Every 8th. row, starting with row 4.              (Pass 2)
      Group 3 : Every 4th. row, starting with row 2.              (Pass 3)
      Group 4 : Every 2nd. row, starting with row 1.              (Pass 4)

The Following example illustrates how the rows of an interlaced image are
ordered.

      Row Number                                        Interlace Pass

 0    -----------------------------------------       1
 1    -----------------------------------------                         4
 2    -----------------------------------------                   3
 3    -----------------------------------------                         4
 4    -----------------------------------------             2
 5    -----------------------------------------                         4
 6    -----------------------------------------                   3
 7    -----------------------------------------                         4
 8    -----------------------------------------       1
 9    -----------------------------------------                         4
 10   -----------------------------------------                   3
 11   -----------------------------------------                         4
 12   -----------------------------------------             2
 13   -----------------------------------------                         4
 14   -----------------------------------------                   3
 15   -----------------------------------------                         4
 16   -----------------------------------------       1
 17   -----------------------------------------                         4
 18   -----------------------------------------                   3
 19   -----------------------------------------                         4

                                                                        30

Appendix
F. Variable-Length-Code LZW Compression.

The Variable-Length-Code LZW Compression is a variation of the Lempel-Ziv
Compression algorithm in which variable-length codes are used to replace
patterns detected in the original data. The algorithm uses a code or
translation table constructed from the patterns encountered in the original
data; each new pattern is entered into the table and its index is used to
replace it in the compressed stream.

The compressor takes the data from the input stream and builds a code or
translation table with the patterns as it encounters them; each new pattern is
entered into the code table and its index is added to the output stream; when a
pattern is encountered which had been detected since the last code table
refresh, its index from the code table is put on the output stream, thus
achieving the data compression.  The expander takes input from the compressed
data stream and builds the code or translation table from it; as the compressed
data stream is processed, codes are used to index into the code table and the
corresponding data is put on the decompressed output stream, thus achieving
data decompression.  The details of the algorithm are explained below.  The
Variable-Length-Code aspect of the algorithm is based on an initial code size
(LZW-initial code size), which specifies the initial number of bits used for
the compression codes.  When the number of patterns detected by the compressor
in the input stream exceeds the number of patterns encodable with the current
number of bits, the number of bits per LZW code is increased by one.

The Raster Data stream that represents the actual output image can be
represented as:

         7 6 5 4 3 2 1 0
        +---------------+
        | LZW code size |
        +---------------+

        +---------------+ ----+
        |  block size   |     |
        +---------------+     |
        |               |     +-- Repeated as many
        |  data bytes   |     |   times as necessary.
        |               |     |
        +---------------+ ----+

        . . .       . . . ------- The code that terminates the LZW
                                  compressed data must appear before
                                  Block Terminator.
        +---------------+
        |0 0 0 0 0 0 0 0|  Block Terminator
        +---------------+

The conversion of the image from a series of pixel values to a transmitted or
stored character stream involves several steps. In brief these steps are:

1. Establish the Code Size - Define the number of bits needed to represent the
actual data.

2. Compress the Data - Compress the series of image pixels to a series of

                                                                        31

compression codes.

3. Build a Series of Bytes - Take the set of compression codes and convert to a
string of 8-bit bytes.

4. Package the Bytes - Package sets of bytes into blocks preceded by character
counts and output.

ESTABLISH CODE SIZE

The first byte of the Compressed Data stream is a value indicating the minimum
number of bits required to represent the set of actual pixel values. Normally
this will be the same as the number of color bits. Because of some algorithmic
constraints however, black & white images which have one color bit must be
indicated as having a code size of 2.
This code size value also implies that the compression codes must start out one
bit longer.

COMPRESSION

The LZW algorithm converts a series of data values into a series of codes which
may be raw values or a code designating a series of values. Using text
characters as an analogy, the output code consists of a character or a code
representing a string of characters.

The LZW algorithm used in GIF matches algorithmically with the standard LZW
algorithm with the following differences:

1.  A special Clear code is defined which resets all compression/decompression
parameters and tables to a start-up state. The value of this code is 2**. For example if the code size indicated was 4 (image was 4 bits/pixel)
the Clear code value would be 16 (10000 binary). The Clear code can appear at
any point in the image data stream and therefore requires the LZW algorithm to
process succeeding codes as if a new data stream was starting. Encoders should
output a Clear code as the first code of each image data stream.

2. An End of Information code is defined that explicitly indicates the end of
the image data stream. LZW processing terminates when this code is encountered.
It must be the last code output by the encoder for an image. The value of this
code is +1.

3. The first available compression code value is +2.

4. The output codes are of variable length, starting at +1 bits per
code, up to 12 bits per code. This defines a maximum code value of 4095
(0xFFF). Whenever the LZW code value would exceed the current code length, the
code length is increased by one. The packing/unpacking of these codes must then
be altered to reflect the new code length.

BUILD 8-BIT BYTES

Because the LZW compression used for GIF creates a series of variable length
codes, of between 3 and 12 bits each, these codes must be reformed into a
series of 8-bit bytes that will be the characters actually stored or
transmitted. This provides additional compression of the image. The codes are
formed into a stream of bits as if they were packed right to left and then

                                                                        32

picked off 8 bits at a time to be output.

Assuming a character array of 8 bits per character and using 5 bit codes to be
packed, an example layout would be similar to:

     +---------------+
  0  |               |    bbbaaaaa
     +---------------+
  1  |               |    dcccccbb
     +---------------+
  2  |               |    eeeedddd
     +---------------+
  3  |               |    ggfffffe
     +---------------+
  4  |               |    hhhhhggg
     +---------------+
           . . .
     +---------------+
  N  |               |
     +---------------+

Note that the physical packing arrangement will change as the number of bits
per compression code change but the concept remains the same.

PACKAGE THE BYTES

Once the bytes have been created, they are grouped into blocks for output by
preceding each block of 0 to 255 bytes with a character count byte. A block
with a zero byte count terminates the Raster Data stream for a given image.
These blocks are what are actually output for the GIF image. This block format
has the side effect of allowing a decoding program the ability to read past the
actual image data if necessary by reading block counts and then skipping over
the data.

FURTHER READING

[1] Ziv, J. and Lempel, A. : "A Universal Algorithm for Sequential Data
Compression", IEEE Transactions on Information Theory, May 1977.
[2] Welch, T. : "A Technique for High-Performance Data Compression", Computer,
June 1984.
[3] Nelson, M.R. : "LZW Data Compression", Dr. Dobb's Journal, October 1989.

                                                                        33

Appendix
G. On-line Capabilities Dialogue.

NOTE : This section is currently (10 July 1990) under revision; the information
provided here should be used as general guidelines. Code written based on this
information should be designed in a flexible way to accommodate any changes
resulting from the revisions.

The following sequences are defined for use in mediating control between a GIF
sender and GIF receiver over an interactive communications line. These
sequences do not apply to applications that involve downloading of static GIF
files and are not considered part of a GIF file.

GIF CAPABILITIES ENQUIRY

The GIF Capabilities Enquiry sequence is issued from a host and requests an
interactive GIF decoder to return a response message that defines the graphics
parameters for the decoder. This involves returning information about available
screen sizes, number of bits/color supported and the amount of color detail
supported. The escape sequence for the GIF Capabilities Enquiry is defined as:

ESC[>0g           0x1B 0x5B 0x3E 0x30 0x67

GIF CAPABILITIES RESPONSE

The GIF Capabilities Response message is returned by an interactive GIF decoder
and defines the decoder's display capabilities for all graphics modes that are
supported by the software. Note that this can also include graphics printers as
well as a monitor screen. The general format of this message is:

#version;protocol{;dev, width, height, color-bits, color-res}...

'#'            GIF Capabilities Response identifier character.
version        GIF format version number;  initially '87a'.
protocol='0'   No end-to-end protocol supported by decoder Transfer as direct
               8-bit data stream.
protocol='1'   Can use CIS B+ error correction protocol to transfer GIF data
               interactively from the host directly to the display.
dev = '0'      Screen parameter set follows.
dev = '1'      Printer parameter set follows.
width          Maximum supported display width in pixels.
height         Maximum supported display height in pixels.
color-bits     Number of bits per pixel supported. The number of supported
               colors is therefore 2**color-bits.
color-res      Number of bits per color component supported in the hardware
               color palette. If color-res is '0' then no hardware palette
               table is available.

Note that all values in the GIF Capabilities Response are returned as ASCII
decimal numbers and the message is terminated by a Carriage Return character.

The following GIF Capabilities Response message describes three standard IBM PC
Enhanced Graphics Adapter configurations with no printer; the GIF data stream
                                                                                34
can be processed within an error correcting protocol:

#87a;1;0,320,200,4,0;0,640,200,2,2;0,640,350,4,2

ENTER GIF GRAPHICS MODE

Two sequences are currently defined to invoke an interactive GIF decoder into
action. The only difference between them is that different output media are
selected. These sequences are:

ESC[>1g     Display GIF image on screen

                  0x1B 0x5B 0x3E 0x31 0x67

ESC[>2g   Display image directly to an attached graphics printer. The image may
optionally be displayed on the screen as well.

                  0x1B 0x5B 0x3E 0x32 0x67

Note that the 'g' character terminating each sequence is in lowercase.

INTERACTIVE ENVIRONMENT

The assumed environment for the transmission of GIF image data from an
interactive application is a full 8-bit data stream from host to micro.  All
256 character codes must be transferrable. The establishing of an 8-bit data
path for communications will normally be taken care of by the host application
programs. It is however up to the receiving communications programs supporting
GIF to be able to receive and pass on all 256 8-bit codes to the GIF decoder
software.


ASM Mode Reel

                   LZW and GIF explained----Steve Blackstock

      I hope this little document will help enlighten those of you out there
who want to know more about the Lempel-Ziv Welch compression algorithm, and,
specifically, the implementation that GIF uses.
     Before we start, here's a little terminology, for the purposes of this
document:

      "character": a fundamental data element. In normal text files, this is
just a single byte. In raster images, which is what we're interested in, it's
an index that specifies the color of a given pixel. I'll refer to an arbitray
character as "K".
      "charstream": a stream of characters, as in a data file.
      "string": a number of continuous characters, anywhere from one to very
many characters in length. I can specify an arbitrary string as "[...]K".
      "prefix": almost the same as a string, but with the implication that a
prefix immediately precedes a character, and a prefix can have a length of
zero. So, a prefix and a character make up a string. I will refer to an
arbitrary prefix as "[...]".
      "root": a single-character string. For most purposes, this is a
character, but we may occasionally make a distinction. It is [...]K, where
[...] is empty.
      "code": a number, specified by a known number of bits, which maps to a
string.
      "codestream": the output stream of codes, as in the "raster data"
      "entry": a code and its string.
      "string table": a list of entries; usually, but not necessarily, unique.
      That should be enough of that.

     LZW is a way of compressing data that takes advantage of repetition of
strings in the data. Since raster data usually contains a lot of this
repetition, LZW is a good way of compressing and decompressing it.
     For the moment, lets consider normal LZW encoding and decoding. GIF's
variation on the concept is just an extension from there.
     LZW manipulates three objects in both compression and decompression: the
charstream, the codestream, and the string table. In compression, the
charstream is the input and the codestream is the output. In decompression,
the codestream is the input and the charstream is the output. The string table
is a product of both compression and decompression, but is never passed from
one to the other.
     The first thing we do in LZW compression is initialize our string table.
To do this, we need to choose a code size (how many bits) and know how many
values our characters can possibly take. Let's say our code size is 12 bits,
meaning we can store 0->FFF, or 4096 entries in our string table. Lets also
say that we have 32 possible different characters. (This corresponds to, say,
a picture in which there are 32 different colors possible for each pixel.) To
initialize the table, we set code#0 to character#0, code #1 to character#1,
and so on, until code#31 to character#31. Actually, we are specifying that
each code from 0 to 31 maps to a root. There will be no more entries in the
table that have this property.
     Now we start compressing data. Let's first define something called the
"current prefix". It's just a prefix that we'll store things in and compare
things to now and then. I will refer to it as "[.c.]". Initially, the current
prefix has nothing in it. Let's also define a "current string", which will be
the current prefix plus the next character in the charstream. I will refer to
the current string as "[.c.]K", where K is some character. OK, look at the
first character in the charstream. Call it P. Make [.c.]P the current string.
(At this point, of course, it's just the root P.) Now search through the
string table to see if [.c.]P appears in it. Of course, it does now, because
our string table is initialized to have all roots. So we don't do anything.
Now make [.c.]P the current prefix. Look at the next character in the
charstream. Call it Q. Add it to the current prefix to form [.c.]Q, the
current string. Now search through the string table to see if [.c.]Q appears
in it. In this case, of course, it doesn't. Aha! Now we get to do something.
Add [.c.]Q (which is PQ in this case) to the string table for code#32, and
output the code for [.c.] to the codestream. Now start over again with the
current prefix being just the root P. Keep adding characters to [.c.] to form
[.c.]K, until you can't find [.c.]K in the string table. Then output the code
for [.c.] and add [.c.]K to the string table. In pseudo-code, the algorithm
goes something like this:

     [1] Initialize string table;
     [2] [.c.] <- empty;
     [3] K <- next character in charstream;
     [4] Is [.c.]K in string table?
      (yes: [.c.] <- [.c.]K;
            go to [3];
      )
      (no: add [.c.]K to the string table;
           output the code for [.c.] to the codestream;
           [.c.] <- K;
           go to [3];
      )

       It's as simple as that! Of course, when you get to step [3] and there
aren't any more characters left, you just output the code for [.c.] and throw
the table away. You're done.
      Wanna do an example? Let's pretend we have a four-character alphabet:
A,B,C,D. The charstream looks like ABACABA. Let's compress it. First, we
initialize our string table to: #0=A, #1=B, #2=C, #3=D. The first character is
A, which is in the string table, so [.c.] becomes A. Next we get AB, which is
not in the table, so we output code #0 (for [.c.]),
     and add AB to the string table as code #4. [.c.] becomes B. Next we get
[.c.]A = BA, which is not in the string table, so output code #1, and add BA
to the string table as code #5. [.c.] becomes A. Next we get AC, which is not
in the string table. Output code #0, and add AC to the string table as code
#6. Now [.c.] becomes C. Next we get [.c.]A = CA, which is not in the table.
Output #2 for C, and add CA to table as code#7. Now [.c.] becomes A. Next we
get AB, which IS in the string table, so [.c.] gets AB, and we look at ABA,
which is not in the string table, so output the code for AB, which is #4, and
add ABA to the string table as code #8. [.c.] becomes A. We can't get any more
characters, so we just output #0 for the code for A, and we're done. So, the
codestream is #0#1#0#2#4#0.
      A few words (four) should be said here about efficiency: use a hashing
strategy. The search through the string table can be computationally
intensive, and some hashing is well worth the effort. Also, note that
"straight LZW" compression runs the risk of overflowing the string table -
getting to a code which can't be represented in the number of bits you've set
aside for codes. There are several ways of dealing with this problem, and GIF
implements a very clever one, but we'll get to that.
      An important thing to notice is that, at any point during the
compression, if [...]K is in the string table, [...] is there also. This fact
suggests an efficient method for storing strings in the table. Rather than
store the entire string of K's in the table, realize that any string can be
expressed as a prefix plus a character: [...]K. If we're about to store [...]K
in the table, we know that [...] is already there, so we can just store the
code for [...] plus the final character K.
      Ok, that takes care of compression. Decompression is perhaps more
difficult conceptually, but it is really easier to program.
      Here's how it goes: We again have to start with an initialized string
table. This table comes from what knowledge we have about the charstream that
we will eventually get, like what possible values the characters can take. In
GIF files, this information is in the header as the number of possible pixel
values. The beauty of LZW, though, is that this is all we need to know. We
will build the rest of the string table as we decompress the codestream. The
compression is done in such a way that we will never encounter a code in the
codestream that we can't translate into a string.
      We need to define something called a "current code", which I will refer
to as "", and an "old-code", which I will refer to as "". To start
things off, look at the first code. This is now . This code will be in
the intialized string table as the code for a root. Output the root to the
charstream. Make this code the old-code . *Now look at the next code, and
make it . It is possible that this code will not be in the string table,
but let's assume for now that it is. Output the string corresponding to
to the codestream. Now find the first character in the string you just
translated. Call this K. Add this to the prefix [...] generated by  to
form a new string [...]K. Add this string [...]K to the string table, and set
the old-code  to the current code . Repeat from where I typed the
asterisk, and you're all set. Read this paragraph again if you just skimmed
it!!!  Now let's consider the possibility that  is not in the string
table. Think back to compression, and try to understand what happens when you
have a string like P[...]P[...]PQ appear in the charstream. Suppose P[...] is
already in the string table, but P[...]P is not. The compressor will parse out
P[...], and find that P[...]P is not in the string table. It will output the
code for P[...], and add P[...]P to the string table. Then it will get up to
P[...]P for the next string, and find that P[...]P is in the table, as
     the code just added. So it will output the code for P[...]P if it finds
that P[...]PQ is not in the table. The decompressor is always "one step
behind" the compressor. When the decompressor sees the code for P[...]P, it
will not have added that code to it's string table yet because it needed the
beginning character of P[...]P to add to the string for the last code, P[...],
to form the code for P[...]P. However, when a decompressor finds a code that
it doesn't know yet, it will always be the very next one to be added to the
string table. So it can guess at what the string for the code should be, and,
in fact, it will always be correct. If I am a decompressor, and I see
code#124, and yet my string table has entries only up to code#123, I can
figure out what code#124 must be, add it to my string table, and output the
string. If code#123 generated the string, which I will refer to here as a
prefix, [...], then code#124, in this special case, will be [...] plus the
first character of [...]. So just add the first character of [...] to the end
of itself. Not too bad.  As an example (and a very common one) of this special
case, let's assume we have a raster image in which the first three pixels have
the same color value. That is, my charstream looks like: QQQ.... For the sake
of argument, let's say we have 32 colors, and Q is the color#12. The
compressor will generate the code sequence 12,32,.... (if you don't know why,
take a minute to understand it.) Remember that #32 is not in the initial
table, which goes from #0 to #31. The decompressor will see #12 and translate
it just fine as color Q. Then it will see #32 and not yet know what that
means. But if it thinks about it long enough, it can figure out that QQ should
be entry#32 in the table and QQ should be the next string output.  So the
decompression pseudo-code goes something like:

      [1] Initialize string table;
     [2] get first code: ;
     [3] output the string for  to the charstream;
     [4]  = ;
     [5]  <- next code in codestream;
     [6] does  exist in the string table?
      (yes: output the string for  to the charstream;
            [...] <- translation for ;
            K <- first character of translation for ;
            add [...]K to the string table;         <- ;  )
      (no: [...] <- translation for ;
           K <- first character of [...];
           output [...]K to charstream and add it to string table;
            <-
      )
     [7] go to [5];

      Again, when you get to step [5] and there are no more codes, you're
finished.  Outputting of strings, and finding of initial characters in strings
are efficiency problems all to themselves, but I'm not going to suggest ways
to do them here. Half the fun of programming is figuring these things out!
      ---
      Now for the GIF variations on the theme. In part of the header of a GIF
file, there is a field, in the Raster Data stream, called "code size". This is
a very misleading name for the field, but we have to live with it. What it is
really is the "root size". The actual size, in bits, of the compression codes
actually changes during compression/decompression, and I will refer to that
size here as the "compression size". The initial table is just the codes for
all the roots, as usual, but two special codes are added on top of those.
Suppose you have a "code size", which is usually the number of bits per pixel
in the image, of N. If the number of bits/pixel is one, then N must be 2: the
roots take up slots #0 and #1 in the initial table, and the two special codes
will take up slots #4 and #5. In any other case, N is the number of bits per
pixel, and the roots take up slots #0 through #(2**N-1), and the special codes
are (2**N) and (2**N + 1). The initial compression size will be N+1 bits per
code. If you're encoding, you output the codes (N+1) bits at a time to start
with, and if you're decoding, you grab (N+1) bits from the codestream at a
time.  As for the special codes:  or the clear code, is (2**N), and ,
or end-of-information, is (2**N + 1).  tells the compressor to re-
initialize the string table, and to reset the compression size to (N+1).
means there's no more in the codestream.  If you're encoding or decoding, you
should start adding things to the string table at  + 2. If you're
encoding, you should output  as the very first code, and then whenever
after that you reach code #4095 (hex FFF), because GIF does not allow
compression sizes to be greater than 12 bits. If you're decoding, you should
reinitialize your string table when you observe .  The variable
compression sizes are really no big deal. If you're encoding, you start with a
compression size of (N+1) bits, and, whenever you output the code
(2**(compression size)-1), you bump the compression size up one bit. So the
next code you output will be one bit longer. Remember that the largest
compression size is 12 bits, corresponding to a code of 4095. If you get that
far, you must output  as the next code, and start over.  If you're
decoding, you must increase your compression size AS SOON AS YOU write entry
#(2**(compression size) - 1) to the string table. The next code you READ will
be one bit longer. Don't make the mistake of waiting until you need to add the
code (2**compression size) to the table. You'll have already missed a bit from
the last code.  The packaging of codes into a bitsream for the raster data is
also a potential stumbling block for the novice encoder or decoder. The lowest
order bit in the code should coincide with the lowest available bit in the
first available byte in the codestream. For example, if you're starting with
5-bit compression codes, and your first three codes are, say, ,
, , where e, j, and o are bit#0, then your codestream will start
off like:

       byte#0: hijabcde
       byte#1: .klmnofg

      So the differences between straight LZW and GIF LZW are: two additional
special codes and variable compression sizes. If you understand LZW, and you
understand those variations, you understand it all!
      Just as sort of a P.S., you may have noticed that a compressor has a
little bit of flexibility at compression time. I specified a "greedy" approach
to the compression, grabbing as many characters as possible before outputting
codes. This is, in fact, the standard LZW way of doing things, and it will
yield the best compression ratio. But there's no rule saying you can't stop
anywhere along the line and just output the code for the current prefix,
whether it's already in the table or not, and add that string plus the next
character to the string table. There are various reasons for wanting to do
this, especially if the strings get extremely long and make hashing difficult.
If you need to, do it.
      Hope this helps out.----steve blackstock


ASM Mode Reel

;===============================================================================
; GIFLIB v1.0 by Exagone - Include file
;===============================================================================
; GIFLIB is (C) Copyright by Exagone. You may freely distribute this library
; according to the licensing defined in the manual.
; [best viewed width tab stop = 4 spaces and syntax highlighting]

;-------------------------------------------------------------------------------
; Function prototypes
;-------------------------------------------------------------------------------

GIFLoadHeader		     	   PROTO	STDCALL lpGifInfo:DWORD
GIFInitalizeDecoder		   PROTO	STDCALL lpGifInfo:DWORD
GIFDecompress		        	PROTO	STDCALL lpGifInfo:DWORD
GIFGetMinimumColorType 	PROTO	STDCALL lpGifInfo:DWORD
GIFGetOutputSize		      PROTO	STDCALL lpGifInfo:DWORD
GIFGetColorTable	      	PROTO	STDCALL lpGifInfo:DWORD, lpBuffer:DWORD
GIFCleanup	          			PROTO	STDCALL lpGifInfo:DWORD
GIFFillBitmapInfoStruct	PROTO	STDCALL lpGifInfo:DWORD, lpBitmapInfoHeader:DWORD
GIFLoadResource	      		PROTO	STDCALL lpGifInfo:DWORD, hInstance:DWORD, lpName:DWORD
GIFLoadFile			         	PROTO	STDCALL lpGifInfo:DWORD, lpFile:DWORD

;-------------------------------------------------------------------------------
; Structures
;-------------------------------------------------------------------------------
GIF_INFO		STRUCT
    ; --- Header ---
    GIFVersion		db	3 dup(?)		; 3 byte string (not 0-terminated) that
                                        ; identifies the GIF Version (like 97a
                                        ; or 98a)
    ; --- Logical screen descriptor ---
    dwLSWidth		dd	?				; Logical screen width
    dwLSHeight		dd	?				; Logical screen height
                                        ; NOTE: Use ImageWidth/Height to find
                                        ; the dimensions, image dimensions can
                                        ; be different from the logical screen
                                        ; width and height.

    ; --- Color info ---
    lpColorTable	dd	?				; Pointer to the color table
    ColorCount		dd	?				; Number of colors in image (and color
                                        ; table)

    ; --- Image info ---
    dwImageWidth	dd	?				; Width of image
    dwImageHeight	dd	?				; Height of image

    ; --- Image data ---
    lpImageData		dd	?				; Pointer to the actual image data
    dwImageFormat	dd	?				; Image format (IMAGEFORMAT_XXXXX)

    ; --- Input data ---
    lpGIFData		dd	?				; Pointer to the GIF File data
    dwGIFDataSize	dd	?				; Size of GIF Data

    ; --- Internal data reserved for giflib ---
    InternalData	db	44 dup (?)
GIF_INFO		ENDS
;-------------------------------------------------------------------------------
; Constants
;-------------------------------------------------------------------------------
IMAGEFORMAT_1BIT	equ 2		 ; 1-bit, monochrome
IMAGEFORMAT_4BIT	equ 16		 ; 4-bit, 16-color
IMAGEFORMAT_8BIT	equ 256		 ; 8-bit, 256-color


ASM Mode Reel

;----------------------------------------------------------------------- BATH
@echo off
ml /c /coff giflib.asm
lib /out:GIFLIB.LIB /LIBPATH:\masm32\lib giflib.obj
pause >nul
@echo off
;----------------------------------------------------------------------- giflib_internal.INC
GIF_INFO		STRUCT
    ; --- Header ---
    GIFVersion		db	3 dup(?)		; 3 byte string (not 0-terminated) that identifies
                                        ; the GIF Version (like 97a or 98a)
    ; --- Logical screen descriptor ---
    dwLSWidth		dd	?				; Logical screen width
    dwLSHeight		dd	?				; Logical screen height
                                        ; NOTE: Use ImageWidth/Height to find the dimensions,
                                        ; This one can be different from the logical screen
                                        ; width and height.

    ; --- Color info ---
    lpColorTable	dd	?				; Pointer to the color table
    ColorCount		dd	?				; Number of colors in image (and color table)

    ; --- Image info ---
    dwImageWidth	dd	?				; Width of image
    dwImageHeight	dd	?				; Height of image

    ; --- Image data ---
    lpImageData		dd	?				; Pointer to the actual image data
    dwImageFormat	dd	?				; Image format (IMAGEFORMAT_XXXXX)

    ; --- Input data ---
    lpGIFData		dd	?				; Pointer to the GIF File data
    dwGIFDataSize	dd	?				; Size of GIF Data

    ; --- Internal data ---
    ; Don't touch this data below, it is used internally by GIFLIB
    lpLZWTable		dd	?				; Pointer to LZW Table
    dwLZWTableSize	dd	?				; Current maximum size of table
                                        ; This size is dynamic and changes
                                        ; if the codesize exceeds 9 bits
    dwLZWTableCount	dd	?				; Current nr of entries in the LZW table
    fGlobalCTable	db	?				; If set, the GIF file has a global color table
    dbGCTableSize	db	?				; Size of the global color table, if available
    fLocalCTable	db	?				; If set, the first image has a local color table
    dbLCTableSize	db	?				; Size of the local color table, if available
    lpGIFImage		dd	?				; Pointer to the first real image data(Table based image data)
    lpCurrentByte	dd	?				; Pointer to the current byte (used with decompression)
                                        ; note: if this value is -1, end of the file is reached and
                                        ; the code-supplier returns 0 as padding
    dbBitsAvailable	db	?				; Nr of codebits currently available
    dbCurBitSize	db	?				; Current LZW code size
    dbInitBitSize	db	?				; Initial LZW code size
    lpLastEOSB		dd	?				; Pointer to end of current subblock (used with decompression)
    dwLZWClear		dd	?				; Value of LZW Clear code (CC)
    CurrentBits		dd	?				; Holds the current bits loaded
    IsFirstCode		db	?				; Indicates if the first code is being read now
    PixelCounter	dd	?				; Counts nr of PIXELS outputted
GIF_INFO		ENDS

IMAGEFORMAT_1BIT	equ 2		 ; 1-bit, monochrome
IMAGEFORMAT_4BIT	equ 16		 ; 4-bit, 16-color
IMAGEFORMAT_8BIT	equ 256		 ; 8-bit, 256-color

EXTENSIONBLOCK				equ		021h
IMAGEDESCRIPTOR				equ		02Ch
GRAPHICCONTROLEXTENSTION	equ		0F9h
COMMENTEXTENSION			equ		0FEh
PLAINTEXTEXTENSION			equ		001h
APPLICATIONEXTENSION		equ		0FFh

;-----------------------------------------------------------------------  GIFLIB.ASM
;===============================================================================
;	GIFLIB v1.0 - (C) Copyright by Exagone
;===============================================================================
; The included general public license (see license.txt) applies to this source
; code. By using this code, you agree to the terms of the license.
;
; Original source code by Exagone
; http://exagone.cjb.net | http://www.exagone.org
; _thomas_@mailroom.com
;
; Libraries and include files from the Win32Asm package by Iczelion:
; http://win32asm.cjb.net/
.486
.model flat, stdcall
option casemap:none

includelib 	kernel32.lib
includelib  user32.lib
includelib 	gdi32.lib

include		\masm32\include\kernel32.inc
include		\masm32\include\user32.inc
include		\masm32\include\gdi32.inc
include		\masm32\include\windows.inc
include		giflib_internal.inc

GIFLoadHeader			PROTO	STDCALL :DWORD
GIFInitalizeDecoder		PROTO	STDCALL :DWORD
GIFDecompress			PROTO	STDCALL :DWORD
SkipExtension			PROTO	STDCALL :DWORD, :DWORD
SkipSubBlocks			PROTO	STDCALL :DWORD
ProcessImageDescriptor	PROTO	STDCALL :DWORD, :DWORD, :DWORD
DECInitialize			PROTO	STDCALL :DWORD
DECResetLZW				PROTO	STDCALL :DWORD
DECDecodeGIF			PROTO	STDCALL :DWORD
DECIsCodeInTable		PROTO	STDCALL :DWORD, :DWORD
DECOutputString			PROTO	STDCALL :DWORD, :DWORD
GetNextCode				PROTO	STDCALL :DWORD
LoadNextByte			PROTO	STDCALL :DWORD
DECAddToTable			PROTO	STDCALL :DWORD, :DWORD, :DWORD
GIFGetMinimumColorType	PROTO	STDCALL :DWORD
GIFGetOutputSize		PROTO	STDCALL :DWORD
GIFGetColorTable		PROTO	STDCALL :DWORD, :DWORD
GIFCleanup				PROTO	STDCALL :DWORD
GIFFillBitmapInfoStruct	PROTO	STDCALL :DWORD, :DWORD
GIFLoadResource			PROTO	STDCALL :DWORD, :DWORD, :DWORD
GIFLoadFile				PROTO	STDCALL :DWORD, :DWORD
ClearMemory				PROTO	STDCALL :DWORD, :DWORD

.code
;===============================================================================
;	ClearMemory
;===============================================================================
; Zeroes memory
ClearMemory		proc	lpMem:DWORD, dwSize:DWORD
    cld
    mov edi, lpMem
    mov ecx, dwSize
    xor	eax, eax
    shr ecx, 2
    rep stosd

    mov ecx, dwSize
    and ecx, 3
    xor	al, al
    rep stosb
    ret
ClearMemory		endp
;===============================================================================
;	GIFLoadResource
;===============================================================================
; Loads gif data from a resource (of type RT_RCDATA)
; lpName:pointer to a resource name OR resource ID
GIFLoadResource proc	lpGifInfo:DWORD, hInst:DWORD, lpName:DWORD
    mov		esi, lpGifInfo
    assume	esi:PTR GIF_INFO

    invoke	FindResource, hInst, lpName, RT_RCDATA
    .IF		eax==0
        ret
    .ENDIF
    push	eax
    invoke	LoadResource, hInst, eax
    .IF		eax==0
        ret
    .ENDIF
    mov		[esi].lpGIFData, eax
    pop		eax
    invoke	SizeofResource, hInst, eax
    .IF		eax==0
        ret
    .ENDIF
    mov		[esi].dwGIFDataSize, eax
    assume	esi:nothing
xor		eax, eax
inc		eax
ret
GIFLoadResource	endp

;===============================================================================
;	GIFLoadFile
;===============================================================================
; Loads gif data from a file
; SHOULD CLEANUP POINTER AFTERWARDS (globalalloc)
GIFLoadFile		proc lpGifInfo:DWORD, lpFile:DWORD
LOCAL	hFile:DWORD
LOCAL	pMem:DWORD
LOCAL	bRead:DWORD
    mov		esi, lpGifInfo
    assume	esi:PTR GIF_INFO
    invoke	CreateFile, lpFile, GENERIC_READ, FILE_SHARE_READ, NULL,\
                OPEN_EXISTING, NULL, NULL
    .IF		eax==INVALID_HANDLE_VALUE
        xor		eax, eax
        ret
    .ENDIF
    mov		hFile, eax
    invoke	GetFileSize, eax, NULL
    .IF		eax==-1
        invoke	CloseHandle, hFile
        xor		eax, eax
        ret
    .ENDIF
    mov		[esi].dwGIFDataSize, eax
    invoke	GlobalAlloc, GMEM_FIXED, eax
    .IF		eax==0
        invoke	CloseHandle, hFile
        xor		eax, eax
        ret
    .ENDIF
    mov		pMem, eax
    invoke	ReadFile, hFile, pMem, [esi].dwGIFDataSize, ADDR bRead, NULL
    .IF		eax==NULL
        invoke	CloseHandle, hFile
        xor		eax, eax
        ret
    .ENDIF
    push	pMem
    pop		[esi].lpGIFData
    invoke	CloseHandle, hFile
    xor		eax, eax
    inc		eax
    assume	esi:nothing
ret
GIFLoadFile		endp
;===============================================================================
;	GIFCleanup
;===============================================================================
; Cleanup
;
GIFCleanup		proc	uses esi lpGifInfo:DWORD
    mov		esi, lpGifInfo
    assume	esi:PTR GIF_INFO
    invoke	GlobalFree, [esi].lpLZWTable
    assume	esi:nothing
ret
GIFCleanup		endp

;===============================================================================
;	GIFGetColorTable
;===============================================================================
; Retrieves the color table to a given buffer, in the RGBQUAD format
;
GIFGetColorTable	proc	uses esi edi ebx lpGifInfo:DWORD, lpBuffer:DWORD

    mov		esi, lpGifInfo
    assume	esi:PTR GIF_INFO

    mov		edi, lpBuffer
    mov		ebx, [esi].lpColorTable
    xor		ecx, ecx
    .WHILE	ecx<[esi].ColorCount
        mov		eax, [ebx]	; RR GG BB 00 -> eax: 00 BB GG RR
        bswap 	eax			; eax: RR GG BB 00
        shr		eax, 8		; eax: 00 RR GG BB (RGBQUAD)
        stosd
        add		ebx, 3
    inc	ecx
    .ENDW
    assume	esi:nothing
ret
GIFGetColorTable	endp
;===============================================================================
;	GIFGetOutputSize
;===============================================================================
; Gets the number of bytes to reserve for the output data
GIFGetOutputSize	proc	uses esi ebx 	lpGifInfo:DWORD
    mov		esi, lpGifInfo
    assume	esi:PTR GIF_INFO

    mov		eax, [esi].dwImageWidth
    mov		ecx, eax

    ; ---Get aligned width ---
    .IF [esi].dwImageFormat==IMAGEFORMAT_1BIT
        and		ecx, 11111b
        .IF		ecx!=0		;
            shr		eax,5
            inc		eax
            shl		eax, 5	;get nr of bytes
        .ENDIF
    .ELSEIF	[esi].dwImageFormat==IMAGEFORMAT_4BIT

        and		ecx, 111b
        .IF		ecx!=0		;
            shr		eax,3
            inc		eax
            shl		eax,3	;get nr of bytes
        .ENDIF
    .ELSEIF [esi].dwImageFormat==IMAGEFORMAT_8BIT
        and		ecx, 11b
        .IF		ecx!=0
            shr		eax, 2
            inc		eax
            shl		eax, 2
        .ENDIF
    .ENDIF

    ; --- multiply output width by nr of scanlines ---
    mul		[esi].dwImageHeight

    assume	esi:nothing
ret
GIFGetOutputSize	endp

;===============================================================================
;	GIFFillBitmapInfoStruct
;===============================================================================
;  Fills a bitmap info structure width the given properties or automatically
GIFFillBitmapInfoStruct	proc	uses esi edi	lpGifInfo:DWORD, lpBIH:DWORD
    mov		esi, lpGifInfo
    mov		edi, lpBIH
    assume	esi:PTR GIF_INFO
    assume	edi:PTR BITMAPINFOHEADER
    mov		[edi].biSize, SIZEOF BITMAPINFOHEADER
    mov		eax,[esi].dwImageHeight
    neg		eax	;negative because it outputs a top-down bitmap
    mov		[edi].biHeight, eax
    push	[esi].dwImageWidth
    pop		[edi].biWidth

    mov		[edi].biPlanes, 1
    mov		eax, [esi].dwImageFormat
    .IF		eax==IMAGEFORMAT_1BIT
        mov		ax, 1
    .ELSEIF eax==IMAGEFORMAT_4BIT
        mov		ax, 4
    .ELSE
        mov		ax, 8
    .ENDIF
    mov		[edi].biBitCount, ax
    xor		eax, eax
    mov		[edi].biCompression, BI_RGB
    mov		[edi].biSizeImage, eax
    mov		[edi].biXPelsPerMeter, eax
    mov		[edi].biYPelsPerMeter, eax
    push	[esi].ColorCount
    pop		[edi].biClrUsed
    mov		[edi].biClrImportant, eax

    assume	esi:nothing
    assume	edi:nothing
ret
GIFFillBitmapInfoStruct	endp
;===============================================================================
;	GIFGetMinimumColorType
;===============================================================================
; Gets the minimum color type (IMAGEFORMAT_XXX) to use for outputting the
; image.
GIFGetMinimumColorType	proc	uses esi	lpGifInfo:DWORD
    mov		esi, lpGifInfo
    assume	esi:PTR GIF_INFO
    mov		eax, [esi].ColorCount
    .IF		eax==2
        mov		eax, IMAGEFORMAT_1BIT
    .ELSEIF	eax<=16
        mov		eax, IMAGEFORMAT_4BIT
    .ELSEIF eax<=256
        mov		eax, IMAGEFORMAT_8BIT
    .ENDIF
    assume	esi:nothing
ret
GIFGetMinimumColorType	endp
;===============================================================================
;	GIFDecompress
;===============================================================================
; Decompresses the gif and outputs
; returns:
; 0  - color format not allowed
; 1  - success
; -1 - failed
GIFDecompress	proc	uses esi edi ebx lpGifInfo:DWORD
    mov		esi, lpGifInfo
    assume	esi:PTR GIF_INFO
    mov		edi, [esi].lpImageData
    ; --- Check if color format is allowed ---
    mov		eax, [esi].dwImageFormat
    .IF		eax<[esi].ColorCount
        xor		eax, eax
        ret
    .ENDIF

    ; --- Initially, load 4 bytes ---
    mov		[esi].CurrentBits, 0
    mov		[esi].dbBitsAvailable, 0
    mov		ebx,4
    .WHILE	ebx>0
        invoke	LoadNextByte, esi
        dec 	ebx
    .ENDW

    invoke	DECInitialize, esi
    invoke	DECDecodeGIF, lpGifInfo

    assume	esi:nothing
xor	eax, eax
inc eax
ret
GIFDecompress	endp
;===============================================================================
;	DECDecodeGIF
;===============================================================================
; The actual decoder
;
DECDecodeGIF	proc	uses esi edi ebx lpGifInfo:DWORD
LOCAL	OldCode:DWORD
LOCAL	FirstCharOfOldCode:BYTE
LOCAL	FirstCharOfCode:BYTE
LOCAL	NrCodesToOutput:DWORD
    mov		esi, lpGifInfo
    assume	esi:PTR GIF_INFO

    ; --- set counters ---
    mov		[esi].PixelCounter, 0	;0 codes outputted
    mov		eax, [esi].dwImageWidth
    mul		[esi].dwImageHeight
    mov		NrCodesToOutput, eax	;this is an OUTPUT CODE counter, not nr of compressed codes
    ; --- clear output mem ---
    invoke	GIFGetOutputSize, lpGifInfo
    mov		ecx, eax
    invoke	ClearMemory, [esi].lpImageData, ecx

;	 LZW Decompression
;     [1] Initialize string table;
;     [2] get first code: ;
;     [3] output the string for  to the charstream;
;     [4]  = ;
;     [5]  <- next code in codestream;
;     [6] does  exist in the string table?
;      (yes: output the string for  to the charstream; (A)
;            [...] <- translation for ;				 (B)
;            K <- first character of translation for ; (C)
;            add [...]K to the string table;    			 (D)
;			  <- ;  )							 (E)
;      (no: [...] <- translation for ;					 (F)
;           K <- first character of [...];					 (G)
;           output [...]K to charstream and add it to string table; (H)
;            <- 									 (I)
;      )
;     [7] go to [5];

    .WHILE	TRUE
        mov		eax, NrCodesToOutput
        .IF		[esi].PixelCounter==eax
            .BREAK	;done
        .ENDIF
        invoke	GetNextCode, esi			; [1] and [5]
        mov		ebx, eax
        .IF		ebx==[esi].dwLZWClear		; Check for clearcode
            invoke	DECResetLZW, esi		; Reset LZW
            .CONTINUE
        .ENDIF
        .IF		[esi].IsFirstCode==1		; Checks if steps [1]-[4] have to be done
            dec		[esi].IsFirstCode		  ;[2]
            invoke	DECOutputString, esi, ebx ;[3]
            mov		FirstCharOfOldCode, al	  ; saved for (G)
            mov		OldCode, ebx			  ;[4]
            .CONTINUE						  ;[5]
        .ENDIF
        invoke	DECIsCodeInTable, esi, ebx	  ;[6]
        .IF		eax==TRUE					  ;(A)-(E)
            invoke	DECOutputString, esi, ebx 	;(B)
            mov		FirstCharOfCode, al			;(C)
            mov		FirstCharOfOldCode, al		; saved for (G)
            invoke	DECAddToTable, esi, OldCode, eax ;(D)
            mov		OldCode, ebx
        .ELSE								  ;(F)-(I)
            mov		al, FirstCharOfOldCode		; (G)
            invoke	DECAddToTable, esi, OldCode, eax ;(H)
            invoke	DECOutputString, esi, ebx   ;(H)
            mov		FirstCharOfOldCode, al		; saved for (G)
            mov		OldCode, ebx
        .ENDIF
    .ENDW									  ;[7]

    assume	esi:nothing
xor	eax, eax
inc eax
ret
DECDecodeGIF	endp

;===============================================================================
; DECAddToTable
;===============================================================================
; Adds an entry to the LZW Table
; The enry consists of a prefix and a suffix (size is calculated automatically)
; returns the code for the new entry
DECAddToTable proc uses esi edi ebx lpGifInfo:DWORD, dwPrefix:DWORD, dbSuffix:DWORD
LOCAL	NewMem:DWORD
LOCAL	OldSize:DWORD
    mov		esi, lpGifInfo
    assume	esi:PTR GIF_INFO

    ; --- Check if enough space in the table left
    mov		ecx, [esi].dwLZWTableSize
    mov		eax, [esi].dwLZWTableCount
    inc		eax
    shl		eax, 2
    ; --- If no space left, make the table bigger:
    .IF		eax==ecx
        mov		OldSize, ecx
        shl		ecx, 1		;Table size doubles
        mov		[esi].dwLZWTableSize, ecx
        ; --- Reallocate memory ---
        mov		eax, [esi].lpLZWTable
        invoke	GlobalAlloc, GMEM_FIXED, ecx
        mov		NewMem, eax
        ; --- Copy old contents to new mem ---
        push	esi
        push	edi
        mov		esi, [esi].lpLZWTable
        mov		edi, NewMem
        mov		ecx, OldSize
        shr		ecx, 2
        rep		movsd
        pop		edi
        pop		esi
        invoke	GlobalFree, [esi].lpLZWTable
        push	NewMem
        pop		[esi].lpLZWTable
    .ENDIF

    ; --- Create pointer to first free entry ---
    mov		ebx, [esi].dwLZWTableCount
    push	ebx
    shl		ebx, 2
    add		ebx, [esi].lpLZWTable

    ; --- Find out the size of the string ---
    mov		eax, dwPrefix
    shl		eax, 2
    add		eax, [esi].lpLZWTable
    mov		eax, [eax]
    shr 	eax, 8
    and		eax, 0FFFh	;eax now holds the size of the prefix string
    inc		eax

    ; --- Create the entry DWORD ---
    ; (see DECResetLZW for the format)
    mov		ecx, dwPrefix
    shl		ecx, 20
    shl		eax, 8
    or		ecx, eax
    mov		eax, dbSuffix
    and		eax, 0ffh
    or		ecx, eax

    ; --- Store entry ---
    mov		[ebx], ecx

    inc		[esi].dwLZWTableCount
    pop		ebx	;get saved code of new entry
    mov		cl, [esi].dbCurBitSize
    xor		eax, eax
    inc		eax
    shl		eax, cl
    dec		eax
    .IF		ebx==eax	;bitsize should be increased
        inc		[esi].dbCurBitSize	;increase bitsize
    .ENDIF

    mov		eax, ebx
ret
DECAddToTable endp

;===============================================================================
; DECOutputString
;===============================================================================
; Writes the string of dwCode to the output stream
; returns the first character of the string in al
DECOutputString		proc uses esi edi  ebx lpGifInfo:DWORD, dwCode:DWORD
LOCAL	LastChar:BYTE
LOCAL	AlignedWidth:DWORD		;image width DWORD  ALIGNED (scanline aligned width)
LOCAL	ImageWidth:DWORD		;actual image width (scanline width)
LOCAL	BitOffset:BYTE
    mov		esi, lpGifInfo
    assume	esi:PTR GIF_INFO

    ; --- Set widths ---
    mov		eax, [esi].dwImageWidth
    mov		ImageWidth, eax
    mov		ecx, eax

    ; --- Set aligned width
    .IF [esi].dwImageFormat==IMAGEFORMAT_1BIT
        and		ecx, 11111b
        .IF		ecx!=0		;
            shr		eax,5
            inc		eax
            shl		eax, 5	;get nr of bytes
        .ENDIF
    .ELSEIF	[esi].dwImageFormat==IMAGEFORMAT_4BIT

        and		ecx, 111b
        .IF		ecx!=0		;
            shr		eax,3
            inc		eax
            shl		eax,3	;get nr of bytes
        .ENDIF
    .ELSEIF [esi].dwImageFormat==IMAGEFORMAT_8BIT
        and		ecx, 11b
        .IF		ecx!=0
            shr		eax, 2
            inc		eax
            shl		eax, 2
        .ENDIF
    .ENDIF
    mov		AlignedWidth, eax

    ; --- Set basepointer ---
    mov		edi, [esi].lpLZWTable

    ; --- Get entry ---
    mov		eax, dwCode
    mov		ebx, [edi+4*eax]

    ; --- Get size of string ---
    shr		ebx, 8
    and		ebx, 0fffh
    ; --- 8-BIT output ---
    .IF		[esi].dwImageFormat==IMAGEFORMAT_8BIT
        ; --- 1-BIT output ---
        mov		edx, [esi].lpImageData
        push	ebx

        ; --- Output all codes backwards ---
        mov		eax, dwCode
        dec		ebx				;use ebx as counter

        .WHILE	TRUE
            mov		eax, [edi+4*eax]
            mov		LastChar, al
            push	eax
            push	edx
            ;--- PixelOffset = (PixelCounter/width)*alignedwidth + (PixelCounter % width) ---
            mov		eax, ebx
            add		eax, [esi].PixelCounter

            cdq
            div		ImageWidth
            mov		ecx, edx
            mul		AlignedWidth
            add		ecx, eax

            mov		al, LastChar
            ;RealByteOffset = PixelOffset
            ;RealBitOffset = 0
            pop		edx
            or		[edx+ecx], al

            pop		eax
            shr		eax, 20
            and		eax, 0fffh
            .BREAK .IF ebx==0
            dec		ebx
        .ENDW
        pop		ebx
        add		[esi].PixelCounter, ebx
        mov		al, LastChar

    .ELSEIF [esi].dwImageFormat==IMAGEFORMAT_4BIT
    ; --- 4-BIT output ---
        mov		edx, [esi].lpImageData
        push	ebx

        ; --- Output all codes backwards ---
        mov		eax, dwCode
        dec		ebx				;use ebx as counter

        .WHILE	TRUE
            mov		eax, [edi+4*eax]
            mov		LastChar, al
            push	eax
            push	edx
            ;--- PixelOffset = (PixelCounter/width)*alignedwidth + (PixelCounter % width) ---
            mov		eax, ebx
            add		eax, [esi].PixelCounter

            cdq
            div		ImageWidth
            mov		ecx, edx
            mul		AlignedWidth
            add		ecx, eax
            mov		edx, ecx

            pop     edx
            ;====
            mov		al, LastChar
            shr		ecx, 1

            .IF		!CARRY?		;use high bits
                shl		al, 4
                or		[edx+ecx], al
            .ELSE
                or		[edx+ecx], al
            .ENDIF

            pop		eax
            shr		eax, 20
            and		eax, 0fffh
            .BREAK .IF ebx==0
            dec		ebx
        .ENDW
        pop		ebx
        add		[esi].PixelCounter, ebx
        mov		al, LastChar
    .ELSEIF [esi].dwImageFormat==IMAGEFORMAT_1BIT
    ; --- 1-BIT output ---
        mov		edx, [esi].lpImageData
        push	ebx

        ; --- Output all codes backwards ---
        mov		eax, dwCode
        dec		ebx				;use ebx as counter

        .WHILE	TRUE
            mov		eax, [edi+4*eax]
            mov		LastChar, al
            push	eax
            push	edx
            ;--- PixelOffset = (PixelCounter/width)*alignedwidth + (PixelCounter % width) ---
            mov		eax, ebx
            add		eax, [esi].PixelCounter

            cdq
            div		ImageWidth
            mov		ecx, edx
            mul		AlignedWidth
            add		ecx, eax
            mov		edx, ecx

            and		cl, 111b	;RealBitOffset  = PixelOffset & 111b;
            mov		al, LastChar
            ror		al, 1
            ror		al, cl

            shr		edx, 3		;RealByteOffset = PixelOffset % 8;
            mov		ecx, edx
            pop		edx
            or		[edx+ecx], al

            pop		eax
            shr		eax, 20
            and		eax, 0fffh
            .BREAK .IF ebx==0
            dec		ebx
        .ENDW
        pop		ebx
        add		[esi].PixelCounter, ebx
        mov		al, LastChar

    .ENDIF
ret
DECOutputString	endp
;===============================================================================
; DECIsCodeInTable
;===============================================================================
; Returns TRUE if the given code is in the string table
;
DECIsCodeInTable	proc	uses esi  ebx lpGifInfo:DWORD, dwCode:DWORD
    mov		esi, lpGifInfo
    assume	esi:PTR GIF_INFO
    mov		ebx, dwCode
    xor		eax, eax
    mov		ecx, [esi].dwLZWTableCount
    .IF		ecx>ebx
        inc		eax
    .ENDIF
    assume	esi:nothing
ret
DECIsCodeInTable	endp
;===============================================================================
; GetNextCode
;===============================================================================
; retrieves the next code from the compressed data stream and loads new bits
; if needed.
GetNextCode		proc	uses esi edi ebx lpGifInfo:DWORD

    mov		esi, lpGifInfo
    assume	esi:PTR GIF_INFO
    ;mov		edi, [esi].lpGIFImage

    mov		ebx, [esi].CurrentBits

    ; --- Create bitmask for current nr of bits ---
    mov		cl,	[esi].dbCurBitSize
    xor		eax, eax
    inc		eax
    shl		eax, cl
    dec		eax

    ; --- AND ebx with bitmask ---
    and		ebx, eax

    ; --- Shift used bits ---
    mov		cl, [esi].dbCurBitSize
    shr		[esi].CurrentBits, cl

    ; --- Decrease # of bits available ---
    sub		[esi].dbBitsAvailable, cl

    ; --- Load as many bits as possible ---
    .WHILE	[esi].dbBitsAvailable<=24
        invoke	LoadNextByte, esi
    .ENDW

    ; --- Return bits ---
    mov		eax, ebx

assume	esi:nothing
ret
GetNextCode		endp

;===============================================================================
; LoadNextByte
;===============================================================================
; Loads the currentbits entry with new bits
;
LoadNextByte	proc	uses esi edi ebx lpGifInfo:DWORD
    mov		esi, lpGifInfo
    assume	esi:PTR GIF_INFO

    ; --- Get next byte from datastream ---
    mov		edi, [esi].lpCurrentByte
    .IF		edi==-1		;end of data already reached
        xor		al, al	;return 0 as padding
        jmp		@gotbyte
    .ENDIF
    ;End of subblock reached?
    .IF		edi==[esi].lpLastEOSB
        xor		eax, eax
        mov		al, [edi]	; new subblock size
        .IF		al==0		; if size=0, end of data
            mov		[esi].lpCurrentByte, -1	;EOF indicator
            xor		al, al
            jmp		@gotbyte
        .ELSE 				; set new end of subblock
            inc		edi		; new subblock with size al
            add		eax, edi
            mov		[esi].lpLastEOSB, eax ;set new end of subblock
            add		[esi].lpCurrentByte, 2
            mov		al, [edi]
        .ENDIF
    .ELSE
        mov		al, [edi]			;get next byte
        inc		[esi].lpCurrentByte	;increase for next read
    .ENDIF

    ; --- Got the new byte from the datasteam ---
@gotbyte:
    mov		cl, [esi].dbBitsAvailable	;Get nr of bits available
    mov		edx, [esi].CurrentBits		;Get current bits
    and		eax, 0ffh					;eax = al
    shl		eax, cl						;Shift bits to right position
    or		edx, eax					;OR new bits with current bits
    mov		[esi].CurrentBits, edx		;Save bits
    add		[esi].dbBitsAvailable, 8	;Set new bits avaialble
    assume	esi:nothing
ret
LoadNextByte	endp
;===============================================================================
;	DECInitialize
;===============================================================================
; Initializes the decoder (table for LZW etc)
;
DECInitialize	proc	uses esi edi lpGifInfo:DWORD
    mov		esi, lpGifInfo
    assume	esi:PTR GIF_INFO
    mov		edi, [esi].lpGIFImage
    ; --- Get initial LZW code size ---
    mov		cl, [edi]
    inc 	cl
    mov		[esi].dbInitBitSize, cl

    ; --- Set current bit size to initial bitsize ---
    mov		[esi].dbCurBitSize, cl

    ; --- Set size of LZW table ---
    .IF		cl<8		;if initial code size is less than 8-bits,
        mov 	cl, 8	;set to 8-bits, if higher, don't change it
    .ENDIF
    ; Calculate size of codetable:
    xor		eax, eax
    inc		eax
    add		cl, 2		; bitsize + 2
    shl		eax, cl		; eax = 2 ^ (bitsize + 2) = 4 * (2^bitsize)
    mov		[esi].dwLZWTableSize, eax

    ; --- Allocate memory for LZW Table and store pointer ---
    invoke	GlobalAlloc, GMEM_FIXED, eax
    mov		[esi].lpLZWTable, eax

    ; --- Reset LZW ---
    invoke	DECResetLZW, esi

    assume	esi:nothing
xor	eax, eax
inc eax
ret
DECInitialize	endp
;===============================================================================
; DECResetLZW
;===============================================================================
; Resets the LZW Table and variables (this is the proper action for a clear
; code (CC) in data
DECResetLZW		proc	uses esi edi ebx lpGifInfo:DWORD
    mov		esi, lpGifInfo
    assume	esi:PTR GIF_INFO
    ; The LZW table has the following format:
    ; An array of DWORDS represents an array of string entries
    ; One entry consists of a prefix code (which is an index to
    ; another entry that should prefix the entry), a string length,
    ; and the suffix byte (this byte should be added to the prefix
    ; to get the full string).
    ; DWORD:
    ; | AAAA AAAA | AAAA BBBB | BBBB BBBB | CCCC CCCC |
    ; ^												^
    ; MSB                                         LSB
    ; AAAAAAAAAAAA:  12-bit index in the LZW Table that indicates the prefix
    ; BBBBBBBBBBBB:  12-bit length of the string
    ; CCCCCCCC	  :   8-bit suffix byte

;int 3
    ; --- reset LZW Table ---
    ; Reset bit size:
    mov		cl, [esi].dbInitBitSize

    mov		[esi].dbCurBitSize, cl
    ; Get initial nr of entries
    ;mov		eax, [esi].ColorCount
    ;NOTE: THE LINE ABOVE WAS PREVIOUSLY USED TO GET THE NR OF ROOT ENTRIES
    ; THIS DIDN'T WORK BECAUSE 1-BIT GIF'S ARE FOR AN UNKNOWN REASON CODED
    ; AS 2-BIT GIFS, SO THE NR OF ROOT ENTRIES HAS TO BE DETERMINED FROM THE
    ; INITIAL BITSIZE
    dec		cl
    xor		eax, eax
    inc		eax
    shl		eax, cl		; 2**codesize
    add		eax, 2
    mov		[esi].dwLZWTableCount, eax
    mov		edx, [esi].lpLZWTable
    ; Fill first {2**codcesize+2} entries
    shl		eax, 2
    add		eax, edx
    xor		ecx, ecx
    inc		ch			;size of sting is set to 1
    .WHILE	edx invalid GIF
        jmp		@failed
    .ENDIF

    ; --- Check first 3 bytes ("GIF" signature) ---
    mov		eax, [edi]
    and		eax, 0ffffffh
    .IF		eax!="FIG"			; GIF Magic signature
        jmp		@failed
    .ENDIF

    ; --- Copy version number ---
    lea		ecx, [esi].GIFVersion
    mov		ax, [edi+3]
    mov		[ecx], ax
    mov		al, [edi+5]
    mov		[ecx+2], al

    ; --- Copy logical screen sizes ---
    xor		eax, eax
    mov		ax, [edi+6]			;Get Logical screen width
    mov		[esi].dwLSWidth, eax
    mov		ax, [edi+8]			;Get Logical screen width
    mov		[esi].dwLSHeight, eax

    ; --- Set global color table flag and size ---
    mov		cl, [edi+10]
    mov		al, cl
    rol		al, 1
    and		al, 1
    mov		[esi].fGlobalCTable, al
    and		cl, 111b
    mov		[esi].dbGCTableSize, cl

    assume 	esi:nothing
xor		eax, eax
inc		eax
ret
@failed:
xor		eax, eax
ret
GIFLoadHeader	endp
;===============================================================================
;	GIFInitalizeDecoder
;===============================================================================
; Initializes decoder
;
GIFInitalizeDecoder	proc	uses esi edi ebx lpGifInfo:DWORD
LOCAL	GCTSize:DWORD
    mov		esi, lpGifInfo
    assume	esi:ptr GIF_INFO

    ; --- EDI points to GIF Data, EBX to end of data ---
    mov		edi, [esi].lpGIFData
    mov		ebx, [esi].dwGIFDataSize
    add		ebx, edi

    add		edi, 13		; skip header and local screen descriptor

    ; --- Get global color tablesize in bytes ---
    mov		cl, [esi].dbGCTableSize
    inc		cl
    xor		eax, eax
    inc		eax
    shl		eax, cl
    mov		ecx, eax
    shl		ecx, 1	;--+--> ecx * 3
    add		ecx, eax;--+
    mov		GCTSize, ecx

    ; --- Skip global color table if available ---
    .IF		[esi].fGlobalCTable!=0
        add		edi, ecx
    .ENDIF
    .IF		edi>=ebx
        jmp		@failed
    .ENDIF

    ; --- Search through the GIF blocks for the first graphic block ---
    .WHILE	edi < ebx
        mov		al,	[edi]					; Get block type
        .IF		al==EXTENSIONBLOCK
            invoke	SkipExtension, edi, ebx
            mov		edi, eax
        .ELSEIF	al==IMAGEDESCRIPTOR
            invoke	ProcessImageDescriptor, lpGifInfo, edi, ebx
            add		edi, 11
            invoke	SkipSubBlocks, edi
            .BREAK
        .ENDIF
    .ENDW

    assume 	esi:nothing
xor		eax, eax
inc		eax
ret
@failed:
xor		eax, eax
ret
GIFInitalizeDecoder	endp

;===============================================================================
; ProcessImageDescriptor
;===============================================================================
; Processes an image desriptor block
ProcessImageDescriptor	proc uses esi edi lpGIFInfo:DWORD, lpStart:DWORD, lpEOF:DWORD
    mov		esi, lpGIFInfo
    assume	esi:ptr GIF_INFO
    mov		edx, lpStart
    xor		eax, eax

    ; --- copy image width & height ---
    mov		ax, [edx+5]
    mov		[esi].dwImageWidth, eax
    mov		ax, [edx+7]
    mov		[esi].dwImageHeight, eax

    ; --- look for local color table ---
    mov		cl, [edx+9]
    mov		al, cl
    rol		cl, 1
    and		cl, 1
    mov		[esi].fLocalCTable, cl
    and		al, 111b
    mov		[esi].dbLCTableSize, al

    ; --- Find out which color table to use ---
    .IF		cl==1		;local color table available
        ; --- Set color count ---
        mov		cl, [esi].dbLCTableSize
        inc		cl
        xor		eax, eax
        inc		eax
        shl		eax, cl
        mov		[esi].ColorCount, eax
        ; --- Set color table pointer ---
        mov		eax, edx
        add		eax, 10	;move to the local color table
        mov		[esi].lpColorTable, eax
    .ELSE				;no local color table, use global color table
        ; --- Set color count ---
        mov		cl, [esi].dbGCTableSize
        inc		cl
        xor		eax, eax
        inc		eax
        shl		eax, cl
        mov		[esi].ColorCount, eax
        ; --- Set color table pointer ---
        mov		eax, [esi].lpGIFData
        add		eax, 13 ;move to the global color table (at offset 13 in file)
        mov		[esi].lpColorTable, eax
    .ENDIF

    ; --- Set pointer to real image data (table based image data)
    mov		eax, edx
    add		eax, 10 				;Skip image descriptor
    .IF		[esi].fLocalCTable==1
        mov		ecx, [esi].ColorCount	;-+
        add		eax, ecx				; + add size of colortable
        shl		ecx, 1					; + (colorcount * 3)
        add		eax, ecx				;-+
    .ENDIF
    mov		[esi].lpGIFImage, eax
    ; --- Set some pointers to initialize the decompressor
    inc		eax
    mov		[esi].lpLastEOSB, eax
    mov		[esi].lpCurrentByte, eax
    assume	esi:nothing
xor eax, eax
inc eax
ret
ProcessImageDescriptor	endp

;
; Skips an extension block that starts at lpStart. EOF is at lpEnd.
; returns pointer to the end of block + 1
; or 0 if failed
SkipExtension proc	lpStart:DWORD, lpEnd:DWORD

    ; --- Get type of extension ---
    mov		edx, lpStart
    mov		al, [edx+1]

    ; --- Skip each type seperately ---
    .IF		al==GRAPHICCONTROLEXTENSTION
        ; Graphic control extension, has a fixed size of 8 bytes
        add		edx, 8
        mov		eax, edx
    .ELSEIF	al==COMMENTEXTENSION
        ; Comment extension, consists of subblocks of text
        add		edx, 2	;move to first subblock
        invoke	SkipSubBlocks, edx

    .ELSEIF	al==PLAINTEXTEXTENSION
        ; Plain text extension, 15 header bytes and subblocks follow
        add		edx, 15
        invoke	SkipSubBlocks, edx

    .ELSEIF al==APPLICATIONEXTENSION
        ; Application extension, 14 header bytes and subblocks follow
        add		edx, 14
        invoke	SkipSubBlocks, edx

    .ENDIF
ret
SkipExtension endp

;===============================================================================
;	SkipSubBlocks
;===============================================================================
; Skips a sequence of subblocks until a block-terminator is found.
; returns a pointer to the end of the block + 1.
; lpStart points to the start of the subblocks
;
SkipSubBlocks	proc lpStart:DWORD
    mov		eax, lpStart
    xor		ecx, ecx
    .WHILE	TRUE
        mov		cl, [eax]	; get size of subblock
        .BREAK .IF cl==0	; size=0 means terminator
        inc		eax
        add		eax, ecx	; add size to pointer
    .ENDW
    inc 	eax
ret
SkipSubBlocks	endp

end


C++Builder the Borland C++ command-line compiler.

*** --- Vu dans bcb5tool.doc 267_Ko --- ***

        15    -  Using IMPLIB
        57    -  Using the module definition file manager (IMPDEF)
        61    -  IMPDEF
        93    -  Classes in a DLL
        132   -  Functions in a DLL

        --------------------------------------------------------------------------

        commande ligne  vu dans Borland C++ BUILDER3 page 425 G鲡rd Leblanc

        C:\implib   toto.lib  ntdll.dll

        Borland Implib Version 3.0.22 Copyright (c) 1991, 2000 Inprise Corporation

        Syntax: IMPLIB [options] libname[.lib] [@respfile | srcname] [srcname ...]
         Options:
            -a     Add '_' alias for MS flavor cdecl functions
            -c     Case sensitive symbols
            -f     Force imports by name (with hints)
            -w     No Warnings

        Respfile may contain a list of source files to process.
        Wildcards are ok for .DLL and .DEF file names.

        --------------------------------------------------------------------------

- Using IMPLIB

The IMPLIB utility creates import library. IMPLIB takes as input DLLs,
module definition files, or both, and produces an import library as output.
If you've created a Windows application, you've already used at least one
import library, IMPORT32.LIB, the import library for the standard Windows DLLs.
IMPORT32.LIB is linked automatically when you build a Win32 application in
the C++Builder IDE and when using BCC32 at the command line.

An import library lists some or all of the exported functions for one or
more DLLs. IMPLIB creates an import
library directly from DLLs or from module definition files for DLLs (or a
combination of the two).
Creating an Import Library for a DLL
Options must be lowercase and preceded by either a hyphen or a slash.
Type:

  IMPLIB Options LibName [ DefFiles... | DLLs... ] [@ResponseFile]

where Options is an optional list of one or more IMPLIB options, LibName is
the name for the new import library, DefFiles is a list of one or more
existing module definition files for one or more DLLs, and DLLs is a list of
one or more existing DLLs.
You must specify at least one DLL or module definition file.
You can also use a response file to list the .DEF and .DLL files that you
want to process. A response file is an ACSII text file that contains a list
of files. The files must be separated by either spaces or new lines in
the file. To specify a response file on the command line, precede the
response filename with an "at" sign (@). For example,

   implib  foo.lib  @respon.txt

Note: A DLL can also have an extension of .EXE or .DRV, not just .DLL.

Option  Description

-a  Add '_' alias for cdecl functions for compatibility with Microsoft libraries
-c  Warnings on case sensitive symbols
-f  Force imports by name
-w  No warnings.

- Using the module definition file manager (IMPDEF)

  Import libraries provide access to the functions in a Windows DLL.

- IMPDEF  takes as input a DLL name, and produces as output a module
  definition file with an export section containing the names of functions
  exported by the DLL.
  The syntax is:

  IMPDEF   DestName.DEF  SourceName.DLL

  This creates a module definition file named DestName.DEF from the file
  SourceName.DLL. The resulting module definition file would look something
  like this:

LIBRARY     FileName
DESCRIPTION 'Description'
EXPORTS
            ExportFuncName              @Ordinal
        .
        .
        .
            ExportFuncName              @Ordinal
where:
 -  FileName is the DLL's root file name
 -  Description is the value of the DESCRIPTION statement if the DLL was
    previously linked with a module definition file that included a
    DESCRIPTION statement
 -  ExportFuncName names an exported function
 -  Ordinal is that function's ordinal value (an integer).

Option  Description

-a  Add '_' alias for cdecl functions for compatibility with Microsoft libraries
-h  Emit hints

- Classes in a DLL

IMPDEF is useful for a DLL that uses C++ classes.
If you use the __declspec (or _export) keyword when defining a class, all of
the non-inline member functions and static data members for that class are
exported. It's easier to let IMPDEF make a module definition file for you
because it lists all the exported functions, and automatically includes the
member functions and static data members.
Since the names of these functions are mangled, it would be tedious to list
them all in the EXPORTS section of a module definition file simply to create
an import library from  the module definition file. If you use
IMPDEF to create the module definition file, it will include the ordinal
value for each exported function. If the exported name is mangled, IMPDEF
will also include that function's  unmangled, original name as a comment
following the function entry. So, for instance, the module definition file
created by IMPDEF for a DLL that used C++ classes would look something
like this:

LIBRARY     FileName
DESCRIPTION 'Description'
EXPORTS
            MangledExportFuncName  @Ordinal ; ExportFuncName
        .
        .
        .
            MangledExportFuncName  @Ordinal ; ExportFuncName

where:
 -  FileName is the DLL's root file name
 -  Description is the value of the DESCRIPTION statement if  the DLL was
    previously linked with a module definition file that included a
    DESCRIPTION statement
 -  MangledExportFuncName provides the mangled name
 -  Ordinal is that function's ordinal value (an integer)
 -  ExportFuncName gives the function's original name.

 - Functions in a DLL

IMPDEF creates an editable source file that lists all the exported
functions in the DLL. You can edit this .DEF file to contain only those
functions that you want to make available  to a particular application, then
run IMPLIB on the edited .DEF file. This results in an import library that
contains import information for a specific subset of a DLL's export functions.
Suppose you're distributing a DLL that provides functions to be used by
several applications.

Every export function in the DLL is defined with __declspec (or _export).

Now, if all the applications used all the DLL's exports, then you could use
IMPLIB to make one import library for the DLL.
You could deliver that import library with the DLL, and it would provide
import information for all of  the DLL's exports. The import library
could be linked to any application, thus eliminating the need for the
particular application to list every DLL function it uses in the IMPORTS
section of its module definition file.
But suppose you want to give only a few of the DLL's exports to a particular
application. Ideally, you want a customized import library to be linked to
that application--an import library that provides import information
only for the subset of functions that the application will use. All of the
other export functions in the DLL will be hidden to that client application.
To create an import library that satisfies these conditions, run IMPDEF on
the compiled and linked DLL.

IMPDEF produces a module definition file that contains an EXPORT section
listing all of the DLL's export functions. You can edit that module
definition file, remove the EXPORTS section entries for those functions
you don't want in the customized import library, and then run IMPLIB on the
module definition file.
The result will be an import library that contains import information for
only those export functions listed in the EXPORTS section of the module
definition file.